Frage

In Sprachen wie Java und C #, sind Strings unveränderlich und kann rechnerisch teuer sein, eine Zeichenfolge ein Zeichen in einer Zeit zu bauen. In diesen Sprachen gibt es Bibliotheksklassen diese Kosten wie C # System.Text.StringBuilder und Java java.lang.StringBuilder.

reduzieren

Ist PHP (4 oder 5, ich habe Interesse an beide) diese Einschränkung teilen? Wenn ja, gibt es ähnliche Lösungen für das Problem zur Verfügung?

War es hilfreich?

Lösung

Nein, es gibt keine Art von Stringbuilder-Klasse in PHP, da Strings wandelbar ist.

Davon abgesehen, gibt es verschiedene Möglichkeiten, einen String erstellen, je nachdem, was Sie tun.

Echo, zum Beispiel, wird durch Komma getrennte Token für die Ausgabe übernehmen.

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

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

Was dies bedeutet, ist, dass man ausgeben kann eine komplexe Zeichenfolge ohne Verkettung tatsächlich zu verwenden, die langsamer sein würde

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

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

Wenn Sie diese Ausgabe in einer Variablen erfassen, dann können Sie das tun, mit dem Ausgabepufferung Funktionen .

Auch ist die PHP-Array-Leistung wirklich gut. Wenn Sie etwas wie eine durch Kommata getrennte Liste von Werten zu tun, nur implodieren verwenden ()

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

Schließlich, stellen Sie sicher, dass Sie machen Sie sich mit PHP-String-Typ und es ist etwas anderes Trennzeichen und die Auswirkungen der einzelnen.

Andere Tipps

Ich war neugierig auf diese, so lief ich einen Test. Ich habe den folgenden Code:

<?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);

... Und bekam die folgenden Ergebnisse. Bild angehängt. Offensichtlich sprintf ist die am wenigsten effiziente Art und Weise, es zu tun, sowohl in Bezug auf Zeit und Speicherverbrauch. EDIT: Ansicht Bild in einem anderen Register, es sei denn Sie haben eagle vision. eingeben Bild Beschreibung hier

Wenn Sie einen zeitlich abgestimmten Vergleich dazu sind die Unterschiede so klein, dass es nicht sehr relevant ist. Es wäre mehr zu machen, da für die Wahl zu gehen, dass Ihr Code leichter zu lesen und verstehen macht.

String analog ist nicht erforderlich in PHP.

Ich habe ein paar einfache Tests:

in 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();

in 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();

Ergebnisse:

10000 Iterationen:
1) PHP, normale Verkettung: ~ 6 ms
2) PHP, mit String: ~ 5 ms
3) C #, normale Verkettung: ~ 520ms
4) C #, mit String: ~ 1 ms

100000 Iterationen:
1) PHP, normale Verkettung: ~ 63ms
2) PHP, mit String: ~ 555ms
3) C #, normale Verkettung: ~ 91000ms // !!!
4) C #, mit String: ~ 17ms

Ich weiß, was du redest. Ich habe gerade diese einfache Klasse, um die Java-Klasse String zu emulieren.

class StringBuilder {

  private $str = array();

  public function __construct() { }

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

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

}

PHP-Strings sind wandelbar. Sie können wie diese bestimmte Zeichen ändern:

$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)

Und Sie können Zeichen in eine Zeichenfolge wie folgt anhängen:

$string .= 'a';

Ja. Tun sie. Für Beispiel, wenn Sie paar Strings echo wollen zusammen mit

echo str1,str2,str3 

statt

echo str1.str2.str3 
, um es ein wenig schneller.

Ich schrieb den Code am Ende dieses Beitrags die verschiedenen Formen der String-Verkettung zu testen und sie sind wirklich alle fast genau gleich in beiden Speichern und Zeit Fußspuren.

Die beiden primären Verfahren I verwendet werden Verketten strings aufeinander und Füllen einer Anordnung mit Zeichenketten und ihnen dann implodiert. Ich habe 500 String-Additionen mit einem 1MB Zeichenfolge in PHP 5.6 (so ist das Ergebnis eine 500MB string). Bei jeder Iteration des Tests, alle Speicher und Zeit Fußspuren waren sehr, sehr nahe (bei ~ $ IterationNumber * 1MB). Die Laufzeit von beiden Tests waren 50,398 Sekunden und 50,843 Sekunden nacheinander die höchstwahrscheinlich innerhalb akzeptabler Fehlergrenzen.

Die Garbage-Collection von Strings, die nicht mehr referenziert werden scheint ziemlich sofort zu sein, auch ohne jemals den Umfang zu verlassen. Da die Saiten wandelbar sind, keine zusätzlichen Speicher wirklich nach der Tat erforderlich ist.

JEDOCH , die folgenden Tests zeigten, dass es eine andere in Spitzenspeichernutzung ist WHILE die Strings werden verkettet.

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

Ergebnis = 10.806.800 Bytes (~ 10MB w / o die anfängliche PHP Speicher-Footprint)

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

Ergebnis = 6.613.320 Bytes (~ 6MB w / o die anfängliche PHP Speicher-Footprint)

So gibt in der Tat ist ein Unterschied, der in sehr, sehr großem Zeichenfolge Verkettungen Speicher-weise von Bedeutung sein könnte (ich in solche Beispiele ausgeführt habe, wenn sehr große Datenmengen oder SQL-Abfragen erstellen).

Aber auch diese Tatsache ist strittig, abhängig von den Daten. Zum Beispiel verketten 1 Zeichen auf einen String erhalten 50 Millionen Bytes (also 50 Millionen Iterationen) eine maximale Menge an 50.322.512 Bytes haben (~ 48MB) in 5,97 Sekunden. Während die Array-Methode zu tun am Ende mit 7337107176 Bytes (~ 6.8GB) dem Array in 12,1 Sekunden zu erstellen, und nahm dann eine zusätzliche 4,32 Sekunden die Saiten aus dem Array zu kombinieren.

Anywho ... die unten ist der Benchmark-Code, den ich am Anfang erwähnt, die zeigt, die Verfahren so ziemlich gleich sind. Es gibt eine ziemlich HTML-Tabelle.

<?
//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>';
}
?>

Erstens, wenn Sie verkettet nicht die Saiten werden müssen, tun Sie es nicht: es wird immer schneller sein zu tun,

echo $a,$b,$c;

als

echo $a . $b . $c;

jedoch zumindest in PHP5, String-Verkettung ist wirklich sehr schnell, vor allem, wenn es nur einen Verweis auf eine bestimmte Zeichenfolge ist. Ich denke, der Interpreter verwendet eine StringBuilder artige Technik intern.

Wenn Sie Variablenwerte innerhalb PHP-Strings sind platzieren, verstehe ich, dass es etwas schneller in-line variable Aufnahme zu verwenden (das ist es nicht offizieller Name ist - ich kann mich nicht erinnern, was ist)

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

muss in doppelten Anführungszeichen zu arbeiten. Funktioniert auch für Array-Mitglieder (d

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

)

diese Einschränkung nicht in php, php kann String mit dem Punkt (.) Operator verketten

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

Ausgänge "Hallo Welt"

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top