Эффективно подсчитывает количество строк текстового файла. (200mb +)

Я только что узнал, что мой скрипт дает мне фатальную ошибку:

Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109 

Эта строка такова:

 $lines = count(file($path)) - 1; 

Поэтому я думаю, что с трудом загружает файл в память и подсчитывает количество строк, есть ли более эффективный способ сделать это без проблем с памятью?

Текстовые файлы, которые мне нужны, чтобы подсчитать количество строк в диапазоне от 2 МБ до 500 МБ. Может быть, Гиг иногда.

Спасибо всем за любую помощь.

Related of "Эффективно подсчитывает количество строк текстового файла. (200mb +)"

Это будет использовать меньше памяти, поскольку он не загружает весь файл в память:

 $file="largefile.txt"; $linecount = 0; $handle = fopen($file, "r"); while(!feof($handle)){ $line = fgets($handle); $linecount++; } fclose($handle); echo $linecount; в $file="largefile.txt"; $linecount = 0; $handle = fopen($file, "r"); while(!feof($handle)){ $line = fgets($handle); $linecount++; } fclose($handle); echo $linecount; 

fgets загружает одну строку в память (если второй аргумент $length опущен, он будет продолжать чтение из потока до тех пор, пока он не достигнет конца строки, что мы и хотим). Это вряд ли будет так же быстро, как использование чего-то другого, кроме PHP, если вы заботитесь о времени на стене, а также об использовании памяти.

Единственная опасность в этом случае – если какие-то строки особенно длинные (что, если вы сталкиваетесь с файлом 2 ГБ без разрывов строк?). В этом случае вам лучше делать это в кусках и подсчитывать символы конца строки:

 $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; в $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; 

Использование цикла вызовов fgets() – прекрасное решение и, самое простое, написать:

  1. хотя внутренне файл читается с использованием буфера 8192 байта, ваш код все равно должен вызывать эту функцию для каждой строки.

  2. технически возможно, что одна строка может быть больше, чем доступная память, если вы читаете двоичный файл.

Этот код считывает файл в кусках по 8 кбайт каждый, а затем подсчитывает количество строк в этом блоке.

 function getLines($file) { $f = fopen($file, 'rb'); $lines = 0; while (!feof($f)) { $lines += substr_count(fread($f, 8192), "\n"); } fclose($f); return $lines; } 

Если средняя длина каждой строки не превышает 4 КБ, вы уже начнете экономить на вызовах функций, и они могут складываться при обработке больших файлов.

эталонный тест

Я проверил тест с файлом 1 ГБ; вот результаты:

  +-------------+------------------+---------+ | This answer | Dominic's answer | wc -l | +------------+-------------+------------------+---------+ | Lines | 3550388 | 3550389 | 3550388 | +------------+-------------+------------------+---------+ | Runtime | 1.055 | 4.297 | 0.587 | +------------+-------------+------------------+---------+ 

Время измеряется в секундах в реальном времени, см. Здесь, какие реальные средства

Если вы используете это на хосте Linux / Unix, самым простым решением будет использование exec() или аналогичного для запуска команды wc -l $path . Просто убедитесь, что вы сначала дезинфицировали $path чтобы убедиться, что это не что-то вроде «/ path / to / file; rm -rf /».

Простое ориентированное решение

 $file = new \SplFileObject('file.extension'); while($file->valid()) $file->fgets(); var_dump($file->key()); 

Обновить

Другой способ сделать это с помощью PHP_INT_MAX в SplFileObject::seek .

 $file = new \SplFileObject('file.extension', 'r'); $file->seek(PHP_INT_MAX); echo $file->key() + 1; 

Существует более быстрый способ, который я обнаружил, что не требует цикла по всему файлу

только на системах * nix , может быть аналогичный способ для Windows …

 $file = '/path/to/your.file'; //Get number of lines $totalLines = intval(exec("wc -l '$file'")); 

Если вы используете PHP 5.5, вы можете использовать генератор . Это НЕ будет работать ни в одной версии PHP до 5.5. От php.net:

«Генераторы предоставляют простой способ реализовать простые итераторы без накладных расходов или сложности реализации класса, реализующего интерфейс Iterator».

 // 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 

Это дополнение к решению Уоллеса де Соузы

Он также пропускает пустые строки при подсчете:

 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; } 

У вас есть несколько вариантов. Во-первых, чтобы увеличить доступную доступную память, что, вероятно, не лучший способ сделать что-то, учитывая, что вы заявляете, что файл может стать очень большим. Другим способом является использование fgets для чтения файла по строке и увеличения счетчика, что не должно вызывать никаких проблем с памятью, поскольку только одна строка в любой момент времени находится в памяти.

 private static function lineCount($file) { $linecount = 0; $handle = fopen($file, "r"); while(!feof($handle)){ if (fgets($handle) !== false) { $linecount++; } } fclose($handle); return $linecount; } в private static function lineCount($file) { $linecount = 0; $handle = fopen($file, "r"); while(!feof($handle)){ if (fgets($handle) !== false) { $linecount++; } } fclose($handle); return $linecount; } 

Я хотел добавить небольшое исправление к функции выше …

в конкретном примере, где у меня был файл, содержащий слово «тестирование», в результате возвращаемая функция. поэтому мне нужно было добавить чек, если fgets вернули false или нет 🙂

повеселись 🙂

Есть еще один ответ, который, как я думал, может быть хорошим дополнением к этому списку.

Если у вас установлен perl и вы можете запускать вещи из оболочки в PHP:

 $lines = exec('perl -pe \'s/\r\n|\n|\r/\n/g\' ' . escapeshellarg('largetextfile.txt') . ' | wc -l'); 

Это должно обрабатывать большинство разрывов строк из файлов Unix или Windows.

ДВЕ минус (по крайней мере):

1) Не стоит отличать ваш сценарий от зависящей от системы работы (небезопасно предположить, что Perl и wc доступны)

2) Небольшая ошибка в побеге, и вы передали доступ к оболочке на своей машине.

Как и большинство вещей, которые я знаю (или думаю, что знаю) о кодировании, я получил эту информацию откуда-то еще:

Статья Джона Рива

 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>"; } в 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>"; } 

Для простого подсчета строк используйте:

 $handle = fopen("file","r"); static $b = 0; while($a = fgets($handle)) { $b++; } echo $b; 

Основываясь на решении Dominic Rodger, я использую то, что я использую (он использует wc, если он доступен, в противном случае отпадает от решения Dominic Rodger).

 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

Я использую этот метод для чисто подсчета количества строк в файле. Что является недостатком в этом стихе других ответов. Я вижу много строк, в отличие от моего двухстрочного решения. Я предполагаю, что есть причина, по которой никто этого не делает.

 $lines = count(file('your.file')); echo $lines;