Эффективно считая количество строк текстового файла. (200mb +) - PullRequest
80 голосов
/ 29 января 2010

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

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 МБ. Может быть, концерт иногда.

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

Ответы [ 16 ]

147 голосов
/ 29 января 2010

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

$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;
100 голосов
/ 12 декабря 2013

Использование цикла из 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 КБ, вы уже начнете экономить на вызовах функций, которые могут сложиться при обработке больших файлов.

Benchmark

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

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

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

41 голосов
/ 24 июля 2015

Простое решение для ориентированных объектов

$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; 
34 голосов
/ 29 января 2010

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

27 голосов
/ 18 марта 2013

Я нашел более быстрый способ, который не требует зацикливания всего файла

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

$file = '/path/to/your.file';

//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));
8 голосов
/ 12 октября 2013

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

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

// 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
5 голосов
/ 28 июня 2017

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

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

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; 
}
3 голосов
/ 25 мая 2018

Если вы используете Linux, вы можете просто сделать:

number_of_lines = intval(trim(shell_exec("wc -l ".$file_name." | awk '{print $1}'")));

Вам просто нужно найти правильную команду, если вы используете другую ОС

Привет

1 голос
/ 02 апреля 2018

Подсчет количества строк можно выполнить следующими кодами:

<?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);
?>
1 голос
/ 30 января 2013
private static function lineCount($file) {
    $linecount = 0;
    $handle = fopen($file, "r");
    while(!feof($handle)){
        if (fgets($handle) !== false) {
                $linecount++;
        }
    }
    fclose($handle);
    return  $linecount;     
}

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

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

веселись :) 1006 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...