Ich habe gerade herausgefunden, dass mein Skript einen schwerwiegenden Fehler verursacht:
Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109
Diese Zeile lautet:
$lines = count(file($path)) - 1;
Ich denke, es ist schwierig, die Datei in den Speicher zu laden und die Anzahl der Zeilen zu zählen. Gibt es eine effizientere Möglichkeit, dies ohne Speicherprobleme zu tun?
Die Textdateien, für die ich die Anzahl der Zeilen zählen muss, reichen von 2 MB bis 500 MB. Vielleicht manchmal ein Gig.
Vielen Dank für jede Hilfe.
\n
) haben, die auf einem Windows-Computer (PHP_EOL == '\r\n'
)fgets($handle, 1);
?substr_count()
Wenn Sie jedoch sehr lange Zeilen haben, müssen Sie anrufenwhile()
undfgets()
vieles mehr, was einen Nachteil verursacht. Vergessen Sie nicht:fgets()
liest nicht Zeile für Zeile. Es liest nur die Anzahl der Zeichen, die Sie definiert haben,$length
und wenn es einen Zeilenumbruch enthält, stoppt es alles$length
, was eingestellt wurde.while(!feof())
Dies führt dazu, dass Sie eine zusätzliche Zeile lesen, da der EOF-Indikator erst gesetzt wird, nachdem Sie versucht haben, am Ende der Datei zu lesen.$line = fgets($handle);
nach nur sein,fgets($handle);
weil$line
es nie verwendet wird.Die Verwendung einer Anrufschleife
fgets()
ist eine gute Lösung und am einfachsten zu schreiben:Obwohl die Datei intern mit einem Puffer von 8192 Bytes gelesen wird, muss Ihr Code diese Funktion für jede Zeile aufrufen.
Es ist technisch möglich, dass eine einzelne Zeile größer ist als der verfügbare Speicher, wenn Sie eine Binärdatei lesen.
Dieser Code liest eine Datei in Blöcken von jeweils 8 KB und zählt dann die Anzahl der Zeilenumbrüche in diesem Block.
Wenn die durchschnittliche Länge jeder Zeile höchstens 4 KB beträgt, sparen Sie bereits bei Funktionsaufrufen. Diese können sich bei der Verarbeitung großer Dateien summieren.
Benchmark
Ich habe einen Test mit einer 1-GB-Datei durchgeführt. Hier sind die Ergebnisse:
Die Zeit wird in Sekunden in Echtzeit gemessen. Sehen Sie hier, was Real bedeutet
quelle
Einfache orientierte Objektlösung
Aktualisieren
Ein anderer Weg , dies zu machen , ist mit
PHP_INT_MAX
in -SplFileObject::seek
Verfahren.quelle
wc -l
(wegen der Verzweigung, nehme ich an), besonders bei kleinen Dateien.Wenn Sie dies auf einem Linux / Unix-Host
exec()
ausführen , ist es am einfachsten , den Befehl zu verwenden oder ähnlichwc -l $path
. Stellen Sie$path
einfach sicher, dass Sie zuerst bereinigt haben , um sicherzustellen, dass es sich nicht um "/ path / to / file; rm -rf /" handelt.quelle
Ich habe einen schnelleren Weg gefunden, bei dem nicht die gesamte Datei durchlaufen werden muss
Nur auf * nix-Systemen kann es unter Windows einen ähnlichen Weg geben ...
quelle
exec('wc -l '.escapeshellarg($file).' 2>/dev/null')
Wenn Sie PHP 5.5 verwenden, können Sie einen Generator verwenden . Dies wird nicht in jeder Version von PHP arbeiten , bevor 5.5 though. Von php.net:
"Generatoren bieten eine einfache Möglichkeit, einfache Iteratoren zu implementieren, ohne den Aufwand oder die Komplexität der Implementierung einer Klasse, die die Iterator-Schnittstelle implementiert."
quelle
try
/finally
ist nicht unbedingt erforderlich, PHP schließt die Datei automatisch für Sie. Sie sollten wahrscheinlich auch erwähnen, dass die eigentliche Zählung mititerator_count(getFiles($file))
:)Dies ist eine Ergänzung zu Wallace de Souzas Lösung
Außerdem werden beim Zählen leere Zeilen übersprungen:
quelle
Wenn Sie unter Linux sind, können Sie einfach Folgendes tun:
Sie müssen nur den richtigen Befehl finden, wenn Sie ein anderes Betriebssystem verwenden
Grüße
quelle
Ich wollte der obigen Funktion eine kleine Korrektur hinzufügen ...
In einem speziellen Beispiel, in dem ich eine Datei hatte, die das Wort 'Testen' enthielt, gab die Funktion als Ergebnis 2 zurück. Also musste ich eine Überprüfung hinzufügen, ob Fgets falsch zurückgegeben wurden oder nicht :)
habe Spaß :)
quelle
Das Zählen der Anzahl der Zeilen kann mit folgenden Codes erfolgen:
quelle
Sie haben mehrere Möglichkeiten. Die erste besteht darin, den verfügbaren verfügbaren Speicher zu erhöhen. Dies ist wahrscheinlich nicht der beste Weg, um Dinge zu tun, da Sie angeben, dass die Datei sehr groß werden kann. Die andere Möglichkeit besteht darin, fgets zu verwenden, um die Datei Zeile für Zeile zu lesen und einen Zähler zu erhöhen , was überhaupt keine Speicherprobleme verursachen sollte, da sich jeweils nur die aktuelle Zeile im Speicher befindet.
quelle
Es gibt noch eine andere Antwort, von der ich dachte, dass sie eine gute Ergänzung zu dieser Liste sein könnte.
Wenn Sie
perl
in PHP Dinge von der Shell installiert haben und ausführen können:Dies sollte die meisten Zeilenumbrüche behandeln, unabhängig davon, ob es sich um von Unix oder Windows erstellte Dateien handelt.
ZWEI Nachteile (mindestens):
1) Es ist keine gute Idee, Ihr Skript so abhängig von dem System zu machen, auf dem es ausgeführt wird (es ist möglicherweise nicht sicher anzunehmen, dass Perl und wc verfügbar sind).
2) Nur ein kleiner Fehler beim Entkommen und Sie haben den Zugriff auf eine Shell auf Ihrer Maschine übergeben.
Wie bei den meisten Dingen, die ich über Codierung weiß (oder zu wissen glaube), habe ich diese Informationen von einem anderen Ort erhalten:
John Reeve Artikel
quelle
quelle
Basierend auf der Lösung von Dominic Rodger verwende ich Folgendes (es verwendet wc, falls verfügbar, andernfalls greift es auf die Lösung von Dominic Rodger zurück).
https://github.com/lingtalfi/Bat/blob/master/FileTool.php
quelle
Ich benutze diese Methode, um nur zu zählen, wie viele Zeilen in einer Datei enthalten sind. Was ist der Nachteil dieses Verses gegenüber den anderen Antworten? Ich sehe viele Zeilen im Gegensatz zu meiner zweizeiligen Lösung. Ich vermute, es gibt einen Grund, warum niemand dies tut.
quelle
Die prägnanteste plattformübergreifende Lösung, die jeweils nur eine Zeile puffert.
Leider müssen wir das
READ_AHEAD
Flag setzen, sonstiterator_count
blockiert es auf unbestimmte Zeit. Andernfalls wäre dies ein Einzeiler.quelle
Um nur die Zeilen zu zählen, verwenden Sie:
quelle