Подтвердить что ты не робот

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

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

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;

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Benchmark

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

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

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

Ответ 3

Решение с простым ориентированным объектом

$file = new \SplFileObject('file.extension');

while($file->valid()) $file->fgets();

var_dump($file->key());

Update

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

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

echo $file->key() + 1; 

Ответ 4

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

Ответ 5

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

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

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

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

Ответ 6

Если вы используете PHP 5.5, вы можете использовать generator. Это будет НЕ работать в любой версии 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

Ответ 7

Это дополнение к решению Wallace de Souza

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

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

Ответ 8

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

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

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

С уважением

Ответ 9

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 или нет:)

получайте удовольствие:)

Ответ 10

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

<?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);
?>

Ответ 11

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

Ответ 12

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

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

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

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

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

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

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

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

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

Ответ 13

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

Ответ 14

Основываясь на доминантном решении Роджера, вот то, что я использую (он использует 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

Ответ 15

Наиболее сжатое кроссплатформенное решение, которое буферизует только одну строку за раз.

$file = new \SplFileObject(__FILE__);
$file->setFlags($file::READ_AHEAD);
$lines = iterator_count($file);

К сожалению, мы должны установить флаг READ_AHEAD, иначе блоки iterator_count будут неопределенным образом. В противном случае это будет однострочник.

Ответ 16

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

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

Ответ 17

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

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