有效地计算文本文件的行数。(200MB +)

php file memory text memory-leaks

124556 观看

17回复

19389 作者的声誉

我刚刚发现我的脚本给了我一个致命的错误:

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;

所以我认为将文件加载到记忆中并计算行数很困难,是否有更有效的方法可以做到这一点而不会出现内存问题?

我需要计算的行数为2MB到500MB的文本文件。也许有时候是Gig。

谢谢大家的帮助。

作者: Abs 的来源 发布者: 2010 年 1 月 29 日

回应 (17)


34

35945 作者的声誉

如果您在Linux / Unix主机上运行它,最简单的解决方案是使用exec()或类似运行命令wc -l $path。只要确保你已经$path首先进行了清理,以确保它不像“/ path / to / file; rm -rf /”。

作者: Dave Sherohman 发布者: 29.01.2010 02:30

144

75158 作者的声誉

决定

这将使用更少的内存,因为它不会将整个文件加载到内存中:

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

fclose($handle);

echo $linecount;

fgets将一行加载到内存中(如果$length省略第二个参数,它将继续从流中读取,直到它到达行的末尾,这就是我们想要的)。如果你关心壁挂时间和内存使用情况,这仍然不如使用PHP之外的其他东西那么快。

唯一的危险是如果任何行特别长(如果你遇到没有换行的2GB文件怎么办?)。在这种情况下,你最好不要在块中啜饮它,并计算行尾字符:

$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;
作者: Dominic Rodger 发布者: 29.01.2010 02:31

0

44061 作者的声誉

你有几个选择。第一种是增加允许的可用内存,这可能不是最好的方法,因为你声明文件可能变得非常大。另一种方法是使用fgets逐行读取文件并递增计数器,这不会导致任何内存问题,因为任何时候只有当前行在内存中。

作者: Yacoby 发布者: 29.01.2010 02:31

1

10445 作者的声誉

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或不:)

玩得开心 :)

作者: ufk 发布者: 30.01.2013 07:38

27

3999 作者的声誉

我发现有一种更快的方法,不需要循环遍历整个文件

仅在* nix系统上,在Windows上可能有类似的方式......

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

//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));
作者: Andy Braham 发布者: 17.03.2013 09:18

8

2525 作者的声誉

如果您使用的是PHP 5.5,则可以使用生成器。这不适用于5.5之前的任何版本的PHP。来自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
作者: Ben Harold 发布者: 12.10.2013 01:53

97

142686 作者的声誉

使用循环fgets()调用是很好的解决方案,并且最直接的编写,但是:

  1. 即使在内部使用8192字节的缓冲区读取文件,您的代码仍然必须为每一行调用该函数。

  2. 从技术上讲,如果您正在读取二进制文件,单行可能比可用内存大。

此代码以每个8kB的块读取文件,然后计算该块中的换行数。

function getLines($file)
{
    $f = fopen($file, 'rb');
    $lines = 0;

    while (!feof($f)) {
        $lines += substr_count(fread($f, 8192), "\n");
    }

    fclose($f);

    return $lines;
}

如果每行的平均长度最多为4kB,则您已经开始保存函数调用,并且在处理大文件时可以加起来。

基准

我用1GB的文件进行了测试; 结果如下:

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

时间以秒为单位实时测量,请参见此处的真实含义

作者: Ja͢ck 发布者: 12.12.2013 07:08

0

1896 作者的声誉

还有另一个答案,我认为可能是这个列表的一个很好的补充。

如果你已经perl安装并且能够在PHP中运行shell中的东西:

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

这应该处理大多数换行符,无论是从Unix还是Windows创建的文件。

两个缺点(至少):

1)让你的脚本依赖于运行的系统并不是一个好主意(假设Perl和wc可用是不安全的)

2)转义中的一个小错误,你已经移植了对机器上的shell的访问权限。

就像我所知道的(或者我认为我知道的)关于编码的大多数事情一样,我从其他地方获得了这些信息:

John Reeve文章

作者: Douglas.Sesar 发布者: 02.08.2014 11:45

0

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>";
}
作者: Yogi Sadhwani 发布者: 28.08.2014 09:02

-1

1 作者的声誉

仅计算线路使用:

$handle = fopen("file","r");
static $b = 0;
while($a = fgets($handle)) {
    $b++;
}
echo $b;
作者: Adeel Ahmad 发布者: 19.02.2015 03:28

39

2067 作者的声誉

简单的面向对象解决方案

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

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

var_dump($file->key());

更新

另一种方法是使用PHP_INT_MAXin SplFileObject::seek方法。

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

echo $file->key() + 1; 
作者: Wallace Maxters 发布者: 24.07.2015 01:18

0

3507 作者的声誉

基于多米尼克罗杰的解决方案,这就是我使用的(如果可用的话,它使用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

作者: ling 发布者: 23.12.2016 07:48

5

102 作者的声誉

这是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; 
}
作者: Jani 发布者: 28.06.2017 07:09

-1

1 作者的声誉

我使用这种方法纯粹计算文件中的行数。这样做的缺点是其他答案。我看到很多行而不是我的两行解决方案。我猜这是没有人这么做的原因。

$lines = count(file('your.file'));
echo $lines;
作者: kaspirtk1 发布者: 26.10.2017 02:24

1

11 作者的声誉

计算行数可以通过以下代码完成:

<?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);
?>
作者: Santosh Kumar 发布者: 02.04.2018 02:34

2

85 作者的声誉

如果你在Linux下,你可以简单地做:

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

如果您正在使用其他操作系统,则必须找到正确的命令

问候

作者: epixilog 发布者: 25.05.2018 08:47

0

1 作者的声誉

<?php
//php is a pure copy of C
$hand = fopen('text.txt' , 'r');
while(!feof(!$hand)) {
$lines = fgets($hand, 1024); { //can limit storage 
    $echo $lines;
}
fclose($hand); //free memory

?>
作者: PHP_a_ paintedover_C 发布者: 16.11.2018 07:51
32x32