An Funktionen wie file() oder file_get_contents() habe ich mich mittlerweile so gewöhnt das ich sie überall benutze. Das funktioniert auch sehr gut wenn man nur kleine Dateien laden möchte, aber bei größeren Files kommen sie an ihre Grenzen. Das Problem ist das die Datei mit einem mal komplett in den Arbeitsspeicher geladen wird und dieser natürlich beschränkt ist. Bisher habe ich dann einfach das Memory Limit erhöht und gut war, aber bei einem 37GB Logfile ging das nicht mehr. PHP bietet dafür aber Abhilfe durch den Einsatz von Streams. Die Datei wird mit fopen zum lesen geöffnet und dann Zeile für Zeile eingelesen und bearbeitet.
So wurde aus diesem Code
$content = trim(file_get_contents($file));
$lines = explode("\n", $content);
foreach($lines AS $line)
{
echo $line . "\n";
}
dieser hier.
$fp = fopen($file, 'r');
if($fp)
{
while(!feof($fp))
{
$line = fgets($fp);
echo $line . "\n";
}
}
Das Memory Limit konnte damit auf 8MB bleiben und das Script verrichtet seine Arbeit. Neben fgets() gibt es auch noch stream_get_line() die das gleiche leistet. Der Unterschied ist das man bei stream_get_line() den Line Separator angeben kann. Schaut man sich die Kommentare auf php.net zu den beiden Befehlen an, dann scheint mal der eine, mal der andere schneller zu sein. Hier sind meine Beobachtungen dazu:
# stream_get_line()
$ time yes "This is a test line" | head -1000000 | \
php -r '$fp=fopen("php://stdin","r"); \
while($line=stream_get_line($fp,65535,"\n")) { 1; } fclose($fp);'
real 0m0.611s
user 0m0.660s
sys 0m0.084s
# fgets()
$ time yes "This is a test line" | head -1000000 | \
php -r '$fp=fopen("php://stdin","r"); \
while($line=fgets($fp,65535)) { 1; } fclose($fp);'
real 0m12.357s
user 0m4.468s
sys 0m0.288s
Scheinbar ist mein System ein stream_get_line() System.
No related posts.