variable una recorrer extraer escapar dentro concatenar comillas caracteres caracter cadena buscar php string concatenation

una - Concatenación de cadenas php, rendimiento



recorrer string php (11)

En idiomas como Java y C #, las cadenas son inmutables y puede ser computacionalmente costoso construir una cadena de caracteres de uno en uno. En dichos idiomas, existen clases de biblioteca para reducir este costo, como C # System.Text.StringBuilder y Java java.lang.StringBuilder .

¿Php (4 o 5, estoy interesado en ambos) comparte esta limitación? Si es así, ¿hay soluciones similares al problema disponible?


Cuando haces una comparación cronometrada, las diferencias son tan pequeñas que no es muy relevante. Haría más desde que eligió la opción que hace que su código sea más fácil de leer y entender.


El análogo StringBuilder no es necesario en PHP.

Hice un par de pruebas simples:

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, concatenación ordinaria: ~ 6 ms
2) PHP, usando StringBuilder: ~ 5 ms
3) C #, concatenación ordinaria: ~ 520ms
4) C #, usando StringBuilder: ~ 1 ms

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


En primer lugar, si no necesita las cadenas para concatenar, no lo haga: siempre será más rápido hacerlo

echo $a,$b,$c;

que

echo $a . $b . $c;

Sin embargo, al menos en PHP5, la concatenación de cadenas es realmente bastante rápida, especialmente si solo hay una referencia a una cadena dada. Supongo que el intérprete usa una técnica similar a StringBuilder internamente.


Escribí el código al final de esta publicación para probar las diferentes formas de concatenación de cadenas y realmente son todas exactamente iguales en las huellas de memoria y tiempo.

Los dos métodos principales que utilicé son concatenar cadenas entre sí, y llenar una matriz con cadenas y luego implosionarlas. Hice 500 adiciones de cadenas con una cadena de 1MB en php 5.6 (por lo que el resultado es una cadena de 500MB). En cada iteración de la prueba, todas las huellas de memoria y tiempo eran muy similares (en ~ $ IterationNumber * 1MB). El tiempo de ejecución de ambas pruebas fue de 50.398 segundos y 50.843 segundos consecutivos, lo más probable dentro de los márgenes de error aceptables.

La recolección de basura de cadenas a las que ya no se hace referencia parece ser bastante inmediata, incluso sin abandonar el alcance. Como las cuerdas son mutables, no se requiere memoria adicional después del hecho.

SIN EMBARGO , las siguientes pruebas mostraron que hay una diferencia en el uso máximo de la memoria MIENTRAS las cadenas se concatenan.

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

Resultado = 10,806,800 bytes (~ 10 MB sin la huella de memoria PHP inicial)

$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 sin la huella de memoria PHP inicial)

De hecho, hay una diferencia que podría ser significativa en las concatenaciones de cadenas muy grandes en cuanto a la memoria (me he encontrado con tales ejemplos al crear conjuntos de datos muy grandes o consultas SQL).

Pero incluso este hecho es discutible dependiendo de los datos. Por ejemplo, concatenar 1 carácter en una cadena para obtener 50 millones de bytes (por lo tanto, 50 millones de iteraciones) tomó una cantidad máxima de 50,322,512 bytes (~ 48 MB) en 5,97 segundos. Al hacer el método de matriz terminó usando 7,337,107,176 bytes (~ 6.8GB) para crear la matriz en 12.1 segundos, y luego tomó 4.32 segundos adicionales para combinar las cadenas de la matriz.

Anywho ... el siguiente es el código de referencia que mencioné al principio, que muestra que los métodos son bastante iguales. Muestra 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>''; } ?>


Las cadenas de PHP son mutables. Puede cambiar 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 puede agregar caracteres a una cadena como esta:

$string .= ''a'';


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

Dicho esto, hay diferentes formas de construir una cuerda, dependiendo de lo que estés haciendo.

echo, por ejemplo, aceptará tokens separados por comas para la salida.

// This... echo ''one'', ''two''; // Is the same as this echo ''one''; echo ''two'';

Lo que esto significa es que puede generar una cadena compleja sin utilizar la concatenación, lo que sería más lento

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

Si necesita capturar esta salida en una variable, puede hacerlo con las funciones de almacenamiento en búfer de salida .

Además, el rendimiento de la matriz de PHP es realmente bueno. Si quieres hacer algo como una lista de valores separados por comas, solo usa implode ()

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

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


Sé de lo que estás hablando. Acabo de crear esta clase simple para emular la clase Java StringBuilder.

class StringBuilder { private $str = array(); public function __construct() { } public function append($str) { $this->str[] = $str; } public function toString() { return implode($this->str); } }


Sí. Ellas hacen. Por ejemplo, si quiere repetir un par de cadenas, use

echo str1,str2,str3

en lugar de

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


Si está colocando valores variables dentro de las cadenas PHP, entiendo que es un poco más rápido usar la inclusión variable en línea (ese no es su nombre oficial, no recuerdo lo que es)

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

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

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

)


Tenía curiosidad por esto, así que hice una prueba. Use 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 obtuve los siguientes resultados. Imagen adjunta. Claramente, sprintf es la forma menos eficiente de hacerlo, tanto en términos de tiempo como de consumo de memoria. EDITAR: ve la imagen en otra pestaña a menos que tengas la visión de águila.


ninguna limitación de este tipo en php, php puede concatenar strng con el operador de punto (.)

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

salidas "hola mundo"