Pregunta

En lenguajes como Java y C#, las cadenas son inmutables y puede ser computacionalmente costoso construir una cadena de un carácter a la vez.En dichas lenguas, hay clases de biblioteca para reducir este costo, tales como C# System.Text.StringBuilder y Java java.lang.StringBuilder.

Hace php (4 o 5;Estoy interesado en ambos) compartir esta limitación?Si es así, ¿existen soluciones similares para el problema a disposición?

¿Fue útil?

Solución

No, no hay ningún tipo de clase stringbuilder en PHP, ya que las cadenas son mutables.

Dicho esto, hay diferentes maneras de construir una cadena, dependiendo de lo que estás haciendo.

eco, por ejemplo, aceptará separados por comas para los símbolos de salida.

// This...
echo 'one', 'two';

// Is the same as this
echo 'one';
echo 'two';

Lo que esto significa es que usted puede dar salida a una cadena compleja sin utilizar realmente la concatenación, que sería más lento

// This...
echo 'one', 'two';

// Is faster than this...
echo 'one' . 'two';

Si usted necesita para capturar este resultado en una variable, que puede hacer que con el funciones de búfer de salida.

También, PHP matriz de rendimiento es realmente bueno.Si quieres hacer algo como una lista separada por comas de los valores, sólo tiene que utilizar implode()

$values = array( 'one', 'two', 'three' );
$valueList = implode( ', ', $values );

Por último, asegúrese de familiarizarse con PHP del tipo de cadena y es delimitadores diferentes, y las implicaciones de cada uno.

Otros consejos

Tenía curiosidad acerca de esto, así que me hizo una prueba.He utilizado el siguiente código:

<?php
ini_set('memory_limit', '1024M');
define ('CORE_PATH', '/Users/foo');
define ('DS', DIRECTORY_SEPARATOR);

$numtests = 1000000;

function test1($numtests)
{
    $CORE_PATH = '/Users/foo';
    $DS = DIRECTORY_SEPARATOR;
    $a = array();

    $startmem = memory_get_usage();
    $a_start = microtime(true);
    for ($i = 0; $i < $numtests; $i++) {
        $a[] = sprintf('%s%sDesktop%sjunk.php', $CORE_PATH, $DS, $DS);
    }
    $a_end = microtime(true);
    $a_mem = memory_get_usage();

    $timeused = $a_end - $a_start;
    $memused = $a_mem - $startmem;

    echo "TEST 1: sprintf()\n";
    echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n";
}

function test2($numtests)
{
    $CORE_PATH = '/Users/shigh';
    $DS = DIRECTORY_SEPARATOR;
    $a = array();

    $startmem = memory_get_usage();
    $a_start = microtime(true);
    for ($i = 0; $i < $numtests; $i++) {
        $a[] = $CORE_PATH . $DS . 'Desktop' . $DS . 'junk.php';
    }
    $a_end = microtime(true);
    $a_mem = memory_get_usage();

    $timeused = $a_end - $a_start;
    $memused = $a_mem - $startmem;

    echo "TEST 2: Concatenation\n";
    echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n";
}

function test3($numtests)
{
    $CORE_PATH = '/Users/shigh';
    $DS = DIRECTORY_SEPARATOR;
    $a = array();

    $startmem = memory_get_usage();
    $a_start = microtime(true);
    for ($i = 0; $i < $numtests; $i++) {
        ob_start();
        echo $CORE_PATH,$DS,'Desktop',$DS,'junk.php';
        $aa = ob_get_contents();
        ob_end_clean();
        $a[] = $aa;
    }
    $a_end = microtime(true);
    $a_mem = memory_get_usage();

    $timeused = $a_end - $a_start;
    $memused = $a_mem - $startmem;

    echo "TEST 3: Buffering Method\n";
    echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n";
}

function test4($numtests)
{
    $CORE_PATH = '/Users/shigh';
    $DS = DIRECTORY_SEPARATOR;
    $a = array();

    $startmem = memory_get_usage();
    $a_start = microtime(true);
    for ($i = 0; $i < $numtests; $i++) {
        $a[] = "{$CORE_PATH}{$DS}Desktop{$DS}junk.php";
    }
    $a_end = microtime(true);
    $a_mem = memory_get_usage();

    $timeused = $a_end - $a_start;
    $memused = $a_mem - $startmem;

    echo "TEST 4: Braced in-line variables\n";
    echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n";
}

function test5($numtests)
{
    $a = array();

    $startmem = memory_get_usage();
    $a_start = microtime(true);
    for ($i = 0; $i < $numtests; $i++) {
        $CORE_PATH = CORE_PATH;
        $DS = DIRECTORY_SEPARATOR;
        $a[] = "{$CORE_PATH}{$DS}Desktop{$DS}junk.php";
    }
    $a_end = microtime(true);
    $a_mem = memory_get_usage();

    $timeused = $a_end - $a_start;
    $memused = $a_mem - $startmem;

    echo "TEST 5: Braced inline variables with loop-level assignments\n";
    echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n";
}

test1($numtests);
test2($numtests);
test3($numtests);
test4($numtests);
test5($numtests);

...Y obtuvo los siguientes resultados.La imagen adjunta.Claramente, sprintf es la forma menos eficaz de hacerlo, tanto en términos de tiempo y el consumo de memoria.EDITAR:ver la imagen en otra pestaña a menos que usted tenga eagle vision.enter image description here

Al hacer un tiempo de comparación, las diferencias son tan pequeñas que no es muy relevante.Tendría más ya que para ir a por la elección que hace el código más fácil de leer y entender.

StringBuilder analógico no es necesario en PHP.

Hice un par de pruebas sencillas:

en PHP:

$iterations = 10000;
$stringToAppend = 'TESTSTR';
$timer = new Timer(); // based on microtime()
$s = '';
for($i = 0; $i < $iterations; $i++)
{
    $s .= ($i . $stringToAppend);
}
$timer->VarDumpCurrentTimerValue();

$timer->Restart();

// Used purlogic's implementation.
// I tried other implementations, but they are not faster
$sb = new StringBuilder(); 

for($i = 0; $i < $iterations; $i++)
{
    $sb->append($i);
    $sb->append($stringToAppend);
}
$ss = $sb->toString();
$timer->VarDumpCurrentTimerValue();

en C# (.NET 4.0):

const int iterations = 10000;
const string stringToAppend = "TESTSTR";
string s = "";
var timer = new Timer(); // based on StopWatch

for(int i = 0; i < iterations; i++)
{
    s += (i + stringToAppend);
}

timer.ShowCurrentTimerValue();

timer.Restart();

var sb = new StringBuilder();

for(int i = 0; i < iterations; i++)
{
    sb.Append(i);
    sb.Append(stringToAppend);
}

string ss = sb.ToString();

timer.ShowCurrentTimerValue();

Resultados:

10000 iteraciones:
1) PHP, ordinario de concatenación:~6ms
2) de PHP, el uso de StringBuilder:~5 ms
3) C#, ordinario de concatenación:~520ms
4) C#, el uso de StringBuilder:~1ms

100000 iteraciones:
1) PHP, ordinario de concatenación:~63ms
2) de PHP, el uso de StringBuilder:~555ms
3) C#, ordinario de concatenación:~91000ms // !!!
4) C#, el uso de StringBuilder:~17ms

Sé de lo que estás hablando.He creado esta sencilla clase para emular el Java de la clase StringBuilder.

class StringBuilder {

  private $str = array();

  public function __construct() { }

  public function append($str) {
    $this->str[] = $str;
  }

  public function toString() {
    return implode($this->str);
  }

}

Las cadenas PHP son mutables.Puede cambiar los caracteres específicos como este:

$string = 'abc';
$string[2] = 'a'; // $string equals 'aba'
$string[3] = 'd'; // $string equals 'abad'
$string[5] = 'e'; // $string equals 'abad e' (fills character(s) in between with spaces)

Y usted puede añadir caracteres de una cadena como esta:

$string .= 'a';

Sí.Que hacen.Por ejemplo, si desea echo par de cadenas, el uso de

echo str1,str2,str3 

en lugar de

echo str1.str2.str3 
para conseguir un poco más rápido.

Escribí el código al final de este post para probar las diferentes formas de concatenación de cadenas y que realmente son todos casi exactamente igual en ambos la memoria y el tiempo de huellas.

Los dos principales métodos que se utilizan son la concatenación de cadenas en cada uno de los otros, y el llenado de una matriz de cadenas y, a continuación, implosión de ellos.Hice 500 cadena de adiciones con un 1MB de cadena en php 5.6 (por lo que el resultado es una cadena de 500MB).En cada iteración de la prueba, toda la memoria y el tiempo de huellas fueron muy muy cerca (a ~$IterationNumber*1MB).El tiempo de ejecución de ambas pruebas fue de 50.398 segundos y 50.843 segundos, consecutivamente, los cuales son probablemente dentro de márgenes aceptables de error.

La recolección de basura de las cadenas que ya no se hace referencia parece ser bastante inmediata, incluso sin salir del ámbito de aplicación.Puesto que las cuerdas son mutables, no de memoria extra, es realmente necesario después del hecho.

Sin EMBARGO, Las siguientes pruebas mostraron que existe una diferente en el pico de uso de memoria MIENTRAS las cadenas se están concatenados.

$OneMB=str_repeat('x', 1024*1024);
$Final=$OneMB.$OneMB.$OneMB.$OneMB.$OneMB;
print memory_get_peak_usage();

Resultado=10,806,800 bytes (~10 MB c/s inicial de memoria de PHP huella)

$OneMB=str_repeat('x', 1024*1024);
$Final=implode('', Array($OneMB, $OneMB, $OneMB, $OneMB, $OneMB));
print memory_get_peak_usage();

Resultado=6,613,320 bytes (~6 MB c/s inicial de memoria de PHP huella)

Así que en realidad hay una diferencia que puede ser significativo en muy muy grande concatenaciones de cadenas de memoria-sabio (he corrido en tales ejemplos, cuando la creación de conjuntos de datos muy grandes o consultas SQL).

Pero incluso este hecho es discutible, dependiendo de los datos.Por ejemplo, la concatenación de 1 carácter en una cadena para obtener 50 millones de bytes (para 50 millones de iteraciones), tuvo un monto máximo de 50,322,512 bytes (~48 MB) en 5.97 segundos.Mientras se hace el método de la matriz terminó con 7,337,107,176 bytes (~6.8 GB) para crear la matriz en el 12,1 segundos y, a continuación, tomó un extra de 4.32 segundos para combinar las cadenas de la matriz.

Anywho...la siguiente referencia es el código que he mencionado al principio, que muestra los métodos son más o menos iguales.Genera una bonita tabla HTML.

<?
//Please note, for the recursion test to go beyond 256, xdebug.max_nesting_level needs to be raised. You also may need to update your memory_limit depending on the number of iterations

//Output the start memory
print 'Start: '.memory_get_usage()."B<br><br>Below test results are in MB<br>";

//Our 1MB string
global $OneMB, $NumIterations;
$OneMB=str_repeat('x', 1024*1024);
$NumIterations=500;

//Run the tests
$ConcatTest=RunTest('ConcatTest');
$ImplodeTest=RunTest('ImplodeTest');
$RecurseTest=RunTest('RecurseTest');

//Output the results in a table
OutputResults(
  Array('ConcatTest', 'ImplodeTest', 'RecurseTest'),
  Array($ConcatTest, $ImplodeTest, $RecurseTest)
);

//Start a test run by initializing the array that will hold the results and manipulating those results after the test is complete
function RunTest($TestName)
{
  $CurrentTestNums=Array();
  $TestStartMem=memory_get_usage();
  $StartTime=microtime(true);
  RunTestReal($TestName, $CurrentTestNums, $StrLen);
  $CurrentTestNums[]=memory_get_usage();

  //Subtract $TestStartMem from all other numbers
  foreach($CurrentTestNums as &$Num)
    $Num-=$TestStartMem;
  unset($Num);

  $CurrentTestNums[]=$StrLen;
  $CurrentTestNums[]=microtime(true)-$StartTime;

  return $CurrentTestNums;
}

//Initialize the test and store the memory allocated at the end of the test, with the result
function RunTestReal($TestName, &$CurrentTestNums, &$StrLen)
{
  $R=$TestName($CurrentTestNums);
  $CurrentTestNums[]=memory_get_usage();
  $StrLen=strlen($R);
}

//Concatenate 1MB string over and over onto a single string
function ConcatTest(&$CurrentTestNums)
{
  global $OneMB, $NumIterations;
  $Result='';
  for($i=0;$i<$NumIterations;$i++)
  {
    $Result.=$OneMB;
    $CurrentTestNums[]=memory_get_usage();
  }
  return $Result;
}

//Create an array of 1MB strings and then join w/ an implode
function ImplodeTest(&$CurrentTestNums)
{
  global $OneMB, $NumIterations;
  $Result=Array();
  for($i=0;$i<$NumIterations;$i++)
  {
    $Result[]=$OneMB;
    $CurrentTestNums[]=memory_get_usage();
  }
  return implode('', $Result);
}

//Recursively add strings onto each other
function RecurseTest(&$CurrentTestNums, $TestNum=0)
{
  Global $OneMB, $NumIterations;
  if($TestNum==$NumIterations)
    return '';

  $NewStr=RecurseTest($CurrentTestNums, $TestNum+1).$OneMB;
  $CurrentTestNums[]=memory_get_usage();
  return $NewStr;
}

//Output the results in a table
function OutputResults($TestNames, $TestResults)
{
  global $NumIterations;
  print '<table border=1 cellspacing=0 cellpadding=2><tr><th>Test Name</th><th>'.implode('</th><th>', $TestNames).'</th></tr>';
  $FinalNames=Array('Final Result', 'Clean');
  for($i=0;$i<$NumIterations+2;$i++)
  {
    $TestName=($i<$NumIterations ? $i : $FinalNames[$i-$NumIterations]);
    print "<tr><th>$TestName</th>";
    foreach($TestResults as $TR)
      printf('<td>%07.4f</td>', $TR[$i]/1024/1024);
    print '</tr>';
  }

  //Other result numbers
  print '<tr><th>Final String Size</th>';
  foreach($TestResults as $TR)
    printf('<td>%d</td>', $TR[$NumIterations+2]);
  print '</tr><tr><th>Runtime</th>';
    foreach($TestResults as $TR)
      printf('<td>%s</td>', $TR[$NumIterations+3]);
  print '</tr></table>';
}
?>

En primer lugar, si usted no necesita las cadenas concatenadas, no lo hagas:siempre será más rápido realizar

echo $a,$b,$c;

de

echo $a . $b . $c;

Sin embargo, al menos en PHP5, la concatenación de cadenas es realmente muy rápido, sobre todo si sólo hay una referencia a una cadena de caracteres.Supongo que el intérprete utiliza un StringBuilder-como técnica internamente.

Si va a colocar los valores de la variable dentro de las cadenas PHP, entiendo que es un poco más rápido para uso en la variable de la línea de inclusión (que no es nombre oficial - no recuerdo cuál es)

$aString = 'oranges';
$compareString = "comparing apples to {$aString}!";
echo $compareString
   comparing apples to oranges!

Debe estar dentro de comillas dobles para el trabajo.También funciona para los miembros de la matriz (es decir,

echo "You requested page id {$_POST['id']}";

)

esta limitación no en php, php puede concatenar strng con el punto(.) operador

$a="hello ";
$b="world";
echo $a.$b;

las salidas de "hola mundo"

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top