我刚刚发现我的剧本给了我一个致命的错误:
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的文本文件。也许有时候是一个演出。
感谢大家的帮助。
答案 0 :(得分:146)
这将使用更少的内存,因为它不会将整个文件加载到内存中:
$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;
答案 1 :(得分:99)
使用fgets()
次调用循环是一个很好的解决方案,但编写起来最直接:
即使在内部使用8192字节的缓冲区读取文件,您的代码仍然必须为每一行调用该函数。
如果您正在阅读二进制文件,那么技术上可能只有一行可用内存。
此代码以每个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 |
+------------+-------------+------------------+---------+
时间以秒为单位实时衡量,请参阅here真实含义
答案 2 :(得分:41)
简单导向对象解决方案
$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;
答案 3 :(得分:34)
如果您在Linux / Unix主机上运行此操作,最简单的解决方案是使用exec()
或类似命令运行命令wc -l $path
。只需确保首先清理$path
以确保它不是“/ path / to / file; rm -rf /".
答案 4 :(得分:27)
我发现有一种更快的方法,不需要循环遍历整个文件
仅在* nix系统上,在Windows上可能会有类似的方式......
$file = '/path/to/your.file';
//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));
答案 5 :(得分:8)
如果您使用的是PHP 5.5,则可以使用generator。这将 NOT 在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
答案 6 :(得分:5)
这是对Wallace de Souza's解决方案的补充
计数时也会跳过空行:
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;
}
答案 7 :(得分:3)
如果你在Linux下,你可以做到:
number_of_lines = intval(trim(shell_exec("wc -l ".$file_name." | awk '{print $1}'")));
如果您正在使用其他操作系统,则必须找到正确的命令
此致
答案 8 :(得分:1)
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:)
玩得开心:)
答案 9 :(得分:1)
可以通过以下代码计算行数:
<?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);
?>
答案 10 :(得分:0)
最简洁的跨平台解决方案,一次只能缓冲一行。
$file = new \SplFileObject(__FILE__);
$file->setFlags($file::READ_AHEAD);
$lines = iterator_count($file);
不幸的是,我们必须设置READ_AHEAD
标志,否则iterator_count
会无限期地阻塞。否则,这将是单线的。
答案 11 :(得分:0)
基于多米尼克罗杰的解决方案, 这是我使用的(它使用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;
}
}
答案 12 :(得分:0)
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>";
}
答案 13 :(得分:0)
还有另一个答案我认为可能是这个列表的一个很好的补充。
如果您已安装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的访问权。
就像我所知道的(或者我认为我知道的)关于编码的大多数事情一样,我从其他地方获得了这些信息:
答案 14 :(得分:0)
您有几种选择。第一种是增加允许的可用内存,这可能不是最好的方法,因为你声明文件可能变得非常大。另一种方法是使用fgets逐行读取文件并递增计数器,这根本不会导致任何内存问题,因为任何时候只有当前行在内存中。
答案 15 :(得分:-1)
仅计算线路使用次数:
$handle = fopen("file","r");
static $b = 0;
while($a = fgets($handle)) {
$b++;
}
echo $b;
答案 16 :(得分:-1)
我使用此方法纯粹计算文件中的行数。这样做的缺点是其他答案。我看到很多行而不是我的两行解决方案。我猜这是没有人这么做的原因。
$lines = count(file('your.file'));
echo $lines;