Efficiently counting the number of lines of a text file. (200mb+)
我刚发现我的脚本给了我一个致命的错误:
1 | Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109 |
这条线是:
因此,我认为将文件加载到内存和计算行数有困难,有没有更有效的方法可以做到这一点而不出现内存问题?
我需要计算从2MB到500MB范围内的行数的文本文件。有时可能是演出。
谢谢大家的帮助。
这将占用较少的内存,因为它不会将整个文件加载到内存中:
1 2 3 4 5 6 7 8 9 10 11 |
唯一的危险是如果任何行特别长(如果遇到没有换行符的2GB文件怎么办?)。在这种情况下,你最好把它分成块,然后计算行尾字符:
1 2 3 4 5 6 7 8 9 10 11 | $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; |
使用一个循环的
即使在内部使用8192字节的缓冲区读取文件,代码仍然必须为每行调用该函数。
从技术上讲,如果您正在读取一个二进制文件,那么一行可能比可用内存大。
这段代码读取一个8KB的文件块,然后计算该块中的换行数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function getLines($file) { $f = fopen($file, 'rb'); $lines = 0; while (!feof($f)) { $lines += substr_count(fread($f, 8192)," "); } fclose($f); return $lines; } |
如果每行的平均长度至多为4KB,那么就已经开始保存函数调用,并且在处理大文件时,这些调用可以相加。
基准我用一个1GB文件运行了一个测试;结果如下:
1 2 3 4 5 6 7 | +-------------+------------------+---------+ | This answer | Dominic's answer | wc -l | +------------+-------------+------------------+---------+ | Lines | 3550388 | 3550389 | 3550388 | +------------+-------------+------------------+---------+ | Runtime | 1.055 | 4.297 | 0.587 | +------------+-------------+------------------+---------+ |
时间是以秒为单位实时测量的,看看这里真正的意思是什么
面向对象的简单解决方案
1 2 3 4 5 |
更新
另一种方法是使用
1 2 3 4 |
如果您在Linux/Unix主机上运行这个命令,最简单的解决方案是使用
我发现有一种更快的方法,不需要在整个文件中循环
只有在*nix系统上,Windows上可能有类似的方法…
1 2 3 4 |
如果您使用的是PHP5.5,那么可以使用生成器。但这在5.5之前的任何版本的PHP中都不起作用。来自PHP.NET:
"生成器提供了一种简单的方法来实现简单的迭代器,而无需实现实现迭代器接口的类的开销或复杂性。"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 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 |
这是华莱士·德索扎解决方案的补充
它还跳过空行数:
1 2 3 4 5 6 7 8 9 | 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,那么只需执行以下操作:
1 |
如果使用其他操作系统,只需找到正确的命令
当做
可以通过以下代码计算行数:
1 2 3 4 5 6 7 8 |
1 2 3 4 5 6 7 8 9 10 11 |
我想在上面的函数中添加一个小补丁…
在一个特定的示例中,我有一个包含单词"testing"的文件,结果函数返回了2。所以我需要添加一个检查fgets是否返回false:)
玩得开心:
你有几个选择。首先是增加允许的可用内存,这可能不是最好的方法,因为您声明文件可能会变得非常大。另一种方法是使用fgets逐行读取文件并增加一个计数器,这不会导致任何内存问题,因为在任何时候只有当前行在内存中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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>"; } |
基于多米尼克·罗杰的解决方案,这里是我使用的(如果可用,它使用wc,否则返回到DominicRodger的解决方案)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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
我认为还有另一个答案可能是这个列表的一个很好的补充。
如果您安装了
1 2 3 4 5 6 |
这应该处理大多数换行符,不管是来自Unix还是Windows创建的文件。
两个缺点(至少):
1)让您的脚本如此依赖于运行它的系统不是一个好主意(假定Perl和WC可用可能不安全)。
2)只是一个小小的逃逸错误,你已经把机器上的外壳交给了你。
正如我知道(或认为我知道)的大多数关于编码的事情一样,我从其他地方得到了这些信息:
约翰·里夫文章
为了计算行数,请使用:
我使用这个方法来计算一个文件中有多少行。这样做的缺点是什么?与其他答案相比。我看到很多行与我的两行解决方案相反。我想没人这么做是有原因的。