Я только что узнал, что мой сценарий выдает фатальную ошибку:
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 МБ. Может быть, иногда концерт.
Спасибо всем за любую помощь.
\n
), анализируемый на машине с Windows (PHP_EOL == '\r\n'
)fgets($handle, 1);
?substr_count()
, но если у вас очень длинные строки, вам нужно позвонитьwhile()
иfgets()
многое другое, что вызывает недостаток. Не забывайте:fgets()
не читает построчно. Он читает только количество символов, которые вы определили,$length
и если он содержит разрыв строки, он останавливает все,$length
что было установлено.while(!feof())
заставит вас прочитать дополнительную строку, потому что индикатор EOF не устанавливается до тех пор, пока вы не попытаетесь прочитать в конце файла.$line = fgets($handle);
может быть простоfgets($handle);
потому, что$line
никогда не используется.Использование цикла
fgets()
вызовов - прекрасное решение, и его проще всего написать:даже если внутренне файл читается с использованием буфера размером 8192 байта, ваш код все равно должен вызывать эту функцию для каждой строки.
технически возможно, что одна строка может быть больше, чем доступная память, если вы читаете двоичный файл.
Этот код считывает файл фрагментами по 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 ГБ; вот результаты:
Время измеряется в секундах в реальном времени, посмотрите здесь, что означает реальное
источник
Простое ориентированное объектное решение
$file = new \SplFileObject('file.extension'); while($file->valid()) $file->fgets(); var_dump($file->key());
Обновить
Другой способ сделать это - использовать метод
PHP_INT_MAX
inSplFileObject::seek
.$file = new \SplFileObject('file.extension', 'r'); $file->seek(PHP_INT_MAX); echo $file->key() + 1;
источник
wc -l
(я полагаю, из-за разветвления), особенно для небольших файлов.Если вы запускаете это на хосте Linux / Unix, самым простым решением будет использование
exec()
или аналогичный запуск командыwc -l $path
. Просто убедитесь, что вы продезинфицировали$path
сначала, чтобы убедиться, что это не что-то вроде "/ path / to / file; rm -rf /".источник
Я обнаружил, что есть более быстрый способ, который не требует перебора всего файла.
только в системах * nix , может быть аналогичный способ в Windows ...
$file = '/path/to/your.file'; //Get number of lines $totalLines = intval(exec("wc -l '$file'"));
источник
exec('wc -l '.escapeshellarg($file).' 2>/dev/null')
Если вы используете 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
источник
try
/finally
Не является строго необходимым, PHP будет автоматически закрывать файл для вас. Вы, вероятно, также должны упомянуть, что фактический подсчет можно сделать с помощьюiterator_count(getFiles($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; }
источник
Если вы используете Linux, вы можете просто сделать:
number_of_lines = intval(trim(shell_exec("wc -l ".$file_name." | awk '{print $1}'")));
Вам просто нужно найти правильную команду, если вы используете другую ОС
С уважением
источник
private static function lineCount($file) { $linecount = 0; $handle = fopen($file, "r"); while(!feof($handle)){ if (fgets($handle) !== false) { $linecount++; } } fclose($handle); return $linecount; }
Я хотел добавить небольшое исправление в функцию выше ...
в конкретном примере, где у меня был файл, содержащий слово «тестирование», функция в результате вернула 2. поэтому мне нужно было добавить проверку, вернул ли fgets false или нет :)
радоваться, веселиться :)
источник
Основываясь на решении Доминика Роджера, вот что я использую (он использует wc, если он доступен, в противном случае - откат к решению Доминика Роджера).
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
источник
Подсчет количества строк можно производить по следующим кодам:
<?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); ?>
источник
У вас есть несколько вариантов. Первый - увеличить разрешенную доступную память, что, вероятно, не лучший способ делать что-то, учитывая, что вы заявляете, что файл может стать очень большим. Другой способ - использовать fgets для чтения файла построчно и увеличения счетчика, что не должно вызывать никаких проблем с памятью, поскольку в любой момент времени в памяти находится только текущая строка.
источник
Есть еще один ответ, который, по моему мнению, может стать хорошим дополнением к этому списку.
Если вы
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>"; }
источник
Я использую этот метод только для подсчета количества строк в файле. Какова обратная сторона этого стиха, другие ответы. Я вижу много строк в отличие от моего двухстрочного решения. Я предполагаю, что есть причина, по которой никто этого не делает.
$lines = count(file('your.file')); echo $lines;
источник
Наиболее емкое кроссплатформенное решение, которое буферизует только одну строку за раз.
$file = new \SplFileObject(__FILE__); $file->setFlags($file::READ_AHEAD); $lines = iterator_count($file);
К сожалению, мы должны установить этот
READ_AHEAD
флаг, иначеiterator_count
блокировка будет бесконечной. В противном случае это было бы однострочным.источник
Для подсчета строк используйте:
$handle = fopen("file","r"); static $b = 0; while($a = fgets($handle)) { $b++; } echo $b;
источник