Właśnie się dowiedziałem, że mój skrypt wyświetla fatalny błąd:
Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109
Ta linia jest taka:
$lines = count(file($path)) - 1;
Więc myślę, że ma trudności z załadowaniem pliku do pamięci i zliczeniem liczby wierszy, czy jest bardziej wydajny sposób, aby to zrobić bez problemów z pamięcią?
Pliki tekstowe, które potrzebuję, aby policzyć liczbę wierszy, mieszczą się w zakresie od 2 MB do 500 MB. Może czasem koncert.
Dziękuję wszystkim za pomoc.
\n
) są przetwarzane na komputerze z systemem Windows (PHP_EOL == '\r\n'
)fgets($handle, 1);
?substr_count()
, ale jeśli masz bardzo długie linie, musisz wywołaćwhile()
ifgets()
wiele więcej, co powoduje wady. Nie zapomnij:fgets()
nie czyta linia po linii. Czyta tylko liczbę znaków, które zdefiniowałeś,$length
a jeśli zawiera podział wiersza, zatrzymuje wszystko, co$length
zostało ustawione.while(!feof())
spowoduje, że przeczytasz dodatkową linię, ponieważ wskaźnik EOF nie jest ustawiony, dopóki nie spróbujesz odczytać końca pliku.$line = fgets($handle);
może być po prostufgets($handle);
dlatego, że$line
nigdy nie jest używany.Używanie pętli
fgets()
wywołań jest dobrym rozwiązaniem i najłatwiejszym do napisania, jednak:mimo że wewnętrznie plik jest odczytywany przy użyciu bufora o wielkości 8192 bajtów, kod nadal musi wywoływać tę funkcję dla każdej linii.
jest technicznie możliwe, że pojedyncza linia może być większa niż dostępna pamięć, jeśli czytasz plik binarny.
Ten kod odczytuje plik w fragmentach po 8kB każdy, a następnie zlicza liczbę nowych wierszy w tym fragmencie.
function getLines($file) { $f = fopen($file, 'rb'); $lines = 0; while (!feof($f)) { $lines += substr_count(fread($f, 8192), "\n"); } fclose($f); return $lines; }
Jeśli średnia długość każdej linii wynosi co najwyżej 4 kB, zaczniesz już oszczędzać na wywołaniach funkcji, a te mogą się sumować podczas przetwarzania dużych plików.
Reper
Przeprowadziłem test z plikiem 1 GB; Oto wyniki:
Czas mierzony jest w sekundach w czasie rzeczywistym, zobacz tutaj, co oznacza prawdziwy
źródło
Rozwiązanie Simple Oriented Object
$file = new \SplFileObject('file.extension'); while($file->valid()) $file->fgets(); var_dump($file->key());
Aktualizacja
Innym sposobem, aby to się
PHP_INT_MAX
wSplFileObject::seek
metodzie.$file = new \SplFileObject('file.extension', 'r'); $file->seek(PHP_INT_MAX); echo $file->key() + 1;
źródło
wc -l
(z powodu rozwidlenia, jak przypuszczam), szczególnie w przypadku małych plików.Jeśli uruchamiasz to na hoście Linux / Unix, najłatwiejszym rozwiązaniem byłoby użycie
exec()
lub podobnego do uruchomienia poleceniawc -l $path
. Po prostu upewnij się, że$path
najpierw zostałeś oczyszczony, aby upewnić się, że nie jest to coś w rodzaju „/ ścieżka / do / pliku; rm -rf /”.źródło
Znalazłem szybszy sposób, który nie wymaga przeglądania całego pliku
tylko na systemach * nix , podobnie może być na Windowsie ...
$file = '/path/to/your.file'; //Get number of lines $totalLines = intval(exec("wc -l '$file'"));
źródło
exec('wc -l '.escapeshellarg($file).' 2>/dev/null')
Jeśli używasz PHP 5.5, możesz użyć generatora . Będzie to nie działa w każdej wersji PHP przed 5,5 chociaż. Z php.net:
„Generatory zapewniają łatwy sposób implementacji prostych iteratorów bez narzutu i złożoności implementacji klasy, która implementuje interfejs Iteratora”.
// 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
źródło
try
/finally
Nie jest to bezwzględnie konieczne, PHP automatycznie zamknie plik dla ciebie. Powinieneś też chyba wspomnieć, że faktyczne liczenie można zrobić za pomocąiterator_count(getFiles($file))
:)Jest to dodatek do Wallace de Souza rozwiązania
Pomija również puste wiersze podczas liczenia:
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; }
źródło
Jeśli korzystasz z Linuksa, możesz po prostu zrobić:
number_of_lines = intval(trim(shell_exec("wc -l ".$file_name." | awk '{print $1}'")));
Musisz tylko znaleźć odpowiednie polecenie, jeśli używasz innego systemu operacyjnego
pozdrowienia
źródło
private static function lineCount($file) { $linecount = 0; $handle = fopen($file, "r"); while(!feof($handle)){ if (fgets($handle) !== false) { $linecount++; } } fclose($handle); return $linecount; }
Chciałem trochę poprawić powyższą funkcję ...
w konkretnym przykładzie, w którym miałem plik zawierający słowo „testowanie”, funkcja zwróciła jako wynik 2. więc musiałem dodać sprawdzenie, czy fgets zwróciły fałsz, czy nie :)
baw się dobrze :)
źródło
Liczenie linii można wykonać za pomocą następujących kodów:
<?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); ?>
źródło
Masz kilka opcji. Pierwszym jest zwiększenie dostępnej dostępnej pamięci, co prawdopodobnie nie jest najlepszym sposobem robienia rzeczy, biorąc pod uwagę, że plik może być bardzo duży. Innym sposobem jest użycie fgets do odczytywania pliku wiersz po wierszu i zwiększania licznika, co nie powinno powodować żadnych problemów z pamięcią, ponieważ w danym momencie w pamięci znajduje się tylko bieżąca linia.
źródło
Jest jeszcze jedna odpowiedź, która moim zdaniem może być dobrym dodatkiem do tej listy.
Jeśli
perl
zainstalowałeś i możesz uruchamiać rzeczy z powłoki w PHP:$lines = exec('perl -pe \'s/\r\n|\n|\r/\n/g\' ' . escapeshellarg('largetextfile.txt') . ' | wc -l');
Powinno to obsłużyć większość znaków końca wierszy, czy to z plików utworzonych w systemie Unix, czy w systemie Windows.
DWA wady (przynajmniej):
1) Nie jest dobrym pomysłem uzależnienie twojego skryptu od systemu, na którym działa (założenie, że Perl i wc są dostępne, może nie być bezpieczne)
2) Tylko mały błąd w ucieczce i przekazałeś dostęp do powłoki na swoim komputerze.
Podobnie jak w przypadku większości rzeczy, które wiem (lub myślę, że wiem) na temat kodowania, otrzymałem te informacje z innego miejsca:
Artykuł Johna Reeve'a
źródło
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>"; }
źródło
W oparciu o rozwiązanie Dominica Rodgera, oto czego używam (używa wc, jeśli jest dostępne, w przeciwnym razie jest to rozwiązanie zastępcze do rozwiązania Dominica Rodgera).
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; } }
https://github.com/lingtalfi/Bat/blob/master/FileTool.php
źródło
Używam tej metody do liczenia liczby wierszy w pliku. Jaka jest wada robienia tego w przypadku innych odpowiedzi. Widzę wiele linii w przeciwieństwie do mojego rozwiązania dwuwierszowego. Domyślam się, że jest powód, dla którego nikt tego nie robi.
$lines = count(file('your.file')); echo $lines;
źródło
Najbardziej zwięzłe rozwiązanie wieloplatformowe, które buforuje tylko jedną linię naraz.
$file = new \SplFileObject(__FILE__); $file->setFlags($file::READ_AHEAD); $lines = iterator_count($file);
Niestety, musimy ustawić
READ_AHEAD
flagę, w przeciwnym razieiterator_count
blokuje się na czas nieokreślony. W przeciwnym razie byłby to jednolinijkowy.źródło
Do liczenia wierszy użyj:
$handle = fopen("file","r"); static $b = 0; while($a = fgets($handle)) { $b++; } echo $b;
źródło