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, die ich brauche, um die Anzahl der Zeilen für den Bereich von 2 MB bis 500 MB zu zählen. Vielleicht manchmal ein Gig.
Vielen Dank für jede Hilfe.
Dadurch wird weniger Speicher benötigt, da nicht die gesamte Datei in den Speicher geladen wird:
$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
$line = fgets($handle);
$linecount++;
}
fclose($handle);
echo $linecount;
fgets
lädt eine einzelne Zeile in den Speicher (wenn das zweite Argument $length
wird weggelassen, es liest so lange aus dem Stream, bis es das Ende der Zeile erreicht (was wir wollen). Es ist immer noch unwahrscheinlich, dass dies so schnell geht wie die Verwendung von etwas anderem als PHP, wenn Sie sowohl die Zeit für die Wand als auch die Speichernutzung berücksichtigen.
Die einzige Gefahr besteht darin, dass Zeilen besonders lang sind (was ist, wenn Sie auf eine 2-GB-Datei ohne Zeilenumbrüche stoßen?). In diesem Fall ist es besser, wenn Sie es in Stücken schlürfen und die Zeichen am Zeilenende zählen:
$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
$line = fgets($handle, 4096);
$linecount = $linecount + substr_count($line, PHP_EOL);
}
fclose($handle);
echo $linecount;
Die Verwendung einer Schleife von fgets()
Aufrufen ist eine gute Lösung und am einfachsten zu schreiben.
auch wenn die Datei intern mit einem Puffer von 8192 Byte 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.
function getLines($file)
{
$f = fopen($file, 'rb');
$lines = 0;
while (!feof($f)) {
$lines += substr_count(fread($f, 8192), "\n");
}
fclose($f);
return $lines;
}
Wenn die durchschnittliche Länge jeder Zeile höchstens 4 KB beträgt, werden Sie bereits mit dem Speichern von Funktionsaufrufen beginnen, und diese können sich summieren, wenn Sie große Dateien verarbeiten.
Ich habe einen Test mit einer 1-GB-Datei durchgeführt. Hier sind die Ergebnisse:
+-------------+------------------+---------+
| This answer | Dominic's answer | wc -l |
+------------+-------------+------------------+---------+
| Lines | 3550388 | 3550389 | 3550388 |
+------------+-------------+------------------+---------+
| Runtime | 1.055 | 4.297 | 0.587 |
+------------+-------------+------------------+---------+
Die Zeit wird in Sekunden in Echtzeit gemessen, siehe hier was real bedeutet
Einfache orientierte Objektlösung
$file = new \SplFileObject('file.extension');
while($file->valid()) $file->fgets();
var_dump($file->key());
Ein anderer Weg, dies zu erreichen, ist mit PHP_INT_MAX
im SplFileObject::seek
Methode.
$file = new \SplFileObject('file.extension', 'r');
$file->seek(PHP_INT_MAX);
echo $file->key() + 1;
Wenn Sie dies auf einem Linux/Unix-Host ausführen, ist die einfachste Lösung die Verwendung von exec()
oder einer ähnlichen Funktion, um den Befehl wc -l $path
Auszuführen. Stellen Sie zunächst sicher, dass Sie $path
Bereinigt haben, um sicherzustellen, dass es sich nicht um "/ path/to/file; rm -rf /" handelt.
Ich habe festgestellt, dass es eine schnellere Methode gibt, bei der nicht die gesamte Datei durchlaufen werden muss
nur auf * nix Systemen, unter Windows könnte es einen ähnlichen Weg geben ...
$file = '/path/to/your.file';
//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));
Wenn Sie PHP 5.5 verwenden, können Sie einen Generator verwenden. Dies wird [~ # ~] nicht [~ # ~ ] funktionieren in jeder Version von PHP vor 5.5. Von php.net:
"Generatoren bieten eine einfache Möglichkeit, einfache Iteratoren zu implementieren, ohne den Aufwand oder die Komplexität, eine Klasse zu implementieren, die die Iterator-Schnittstelle implementiert."
// This function implements a generator to load individual lines of a large file
function getLines($file) {
$f = fopen($file, 'r');
// read each line of the file without loading the whole file to memory
while ($line = fgets($f)) {
yield $line;
}
}
// Since generators implement simple iterators, I can quickly count the number
// of lines using the iterator_count() function.
$file = '/path/to/file.txt';
$lineCount = iterator_count(getLines($file)); // the number of lines in the file
Dies ist eine Ergänzung zu Wallace de Souza's
Beim Zählen werden auch leere Zeilen übersprungen:
function getLines($file)
{
$file = new \SplFileObject($file, 'r');
$file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY |
SplFileObject::DROP_NEW_LINE);
$file->seek(PHP_INT_MAX);
return $file->key() + 1;
}
Wenn Sie unter Linux arbeiten, können Sie einfach Folgendes tun:
number_of_lines = intval(trim(Shell_exec("wc -l ".$file_name." | awk '{print $1}'")));
Sie müssen nur den richtigen Befehl finden, wenn Sie ein anderes Betriebssystem verwenden
Grüße
Das Zählen der Zeilenanzahl kann mit folgenden Codes erfolgen:
<?php
$fp= fopen("myfile.txt", "r");
$count=0;
while($line = fgetss($fp)) // fgetss() is used to get a line from a file ignoring html tags
$count++;
echo "Total number of lines are ".$count;
fclose($fp);
?>
private static function lineCount($file) {
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
if (fgets($handle) !== false) {
$linecount++;
}
}
fclose($handle);
return $linecount;
}
Ich wollte der obigen Funktion eine kleine Verbesserung hinzufügen ...
in einem bestimmten Beispiel, in dem ich eine Datei hatte, die das Wort "testing" enthielt, gab die Funktion als Ergebnis 2 zurück. Also musste ich eine Prüfung hinzufügen, ob fgets falsch zurückgegeben hat oder nicht :)
habe Spaß :)
Es gibt eine andere Antwort, die ich für eine gute Ergänzung dieser Liste hielt.
Wenn Sie Perl
installiert haben und in der Lage sind, Dinge in der Shell in PHP auszuführen:
$lines = exec('Perl -pe \'s/\r\n|\n|\r/\n/g\' ' . escapeshellarg('largetextfile.txt') . ' | wc -l');
Dies sollte die meisten Zeilenumbrüche behandeln, unabhängig davon, ob es sich um unter 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 bei der Flucht und Sie haben den Zugriff auf eine Shell auf Ihrem Computer übergeben.
Wie bei den meisten Dingen, die ich über das Codieren weiß (oder zu wissen glaube), habe ich diese Informationen von einer anderen Stelle erhalten:
Sie haben mehrere Möglichkeiten. Die erste Möglichkeit besteht darin, den verfügbaren Speicher zu erhöhen. Dies ist wahrscheinlich nicht die beste Möglichkeit, wenn Sie angeben, dass die Datei sehr groß werden kann. Die andere Möglichkeit besteht darin, fgets zu verwenden, um die Datei zeilenweise zu lesen und einen Zähler zu erhöhen. Dies sollte zu keinerlei Speicherproblemen führen, da sich zu jedem Zeitpunkt nur die aktuelle Zeile im Speicher befindet.
public function quickAndDirtyLineCounter()
{
echo "<table>";
$folders = ['C:\wamp\www\qa\abcfolder\',
];
foreach ($folders as $folder) {
$files = scandir($folder);
foreach ($files as $file) {
if($file == '.' || $file == '..' || !file_exists($folder.'\\'.$file)){
continue;
}
$handle = fopen($folder.'/'.$file, "r");
$linecount = 0;
while(!feof($handle)){
if(is_bool($handle)){break;}
$line = fgets($handle);
$linecount++;
}
fclose($handle);
echo "<tr><td>" . $folder . "</td><td>" . $file . "</td><td>" . $linecount . "</td></tr>";
}
}
echo "</table>";
}
Basierend auf der Lösung von dominic Rodger verwende ich Folgendes (es wird wc verwendet, sofern verfügbar, andernfalls wird auf die Lösung von dominic Rodger zurückgegriffen).
class FileTool
{
public static function getNbLines($file)
{
$linecount = 0;
$m = exec('which wc');
if ('' !== $m) {
$cmd = 'wc -l < "' . str_replace('"', '\\"', $file) . '"';
$n = exec($cmd);
return (int)$n + 1;
}
$handle = fopen($file, "r");
while (!feof($handle)) {
$line = fgets($handle);
$linecount++;
}
fclose($handle);
return $linecount;
}
}