Abs*_*Abs 81 php memory text memory-leaks file
我刚刚发现我的脚本给了我一个致命的错误:
Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109
Run Code Online (Sandbox Code Playgroud)
那条线是这样的:
$lines = count(file($path)) - 1;
Run Code Online (Sandbox Code Playgroud)
所以我认为将文件加载到记忆中并计算行数很困难,是否有更有效的方法可以做到这一点而不会出现内存问题?
我需要计算的行数为2MB到500MB的文本文件.也许有时候是Gig.
谢谢大家的帮助.
Dom*_*ger 152
这将使用更少的内存,因为它不会将整个文件加载到内存中:
$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
$line = fgets($handle);
$linecount++;
}
fclose($handle);
echo $linecount;
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
Ja͢*_*͢ck 103
使用循环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;
}
Run Code Online (Sandbox Code Playgroud)
如果每行的平均长度最多为4kB,则您已经开始保存函数调用,并且在处理大文件时可以加起来.
我用1GB的文件进行了测试; 结果如下:
+-------------+------------------+---------+
| This answer | Dominic's answer | wc -l |
+------------+-------------+------------------+---------+
| Lines | 3550388 | 3550389 | 3550388 |
+------------+-------------+------------------+---------+
| Runtime | 1.055 | 4.297 | 0.587 |
+------------+-------------+------------------+---------+
Run Code Online (Sandbox Code Playgroud)
时间以秒为单位实时测量,请参见此处的真实含义
Wal*_*ers 44
简单的面向对象解决方案
$file = new \SplFileObject('file.extension');
while($file->valid()) $file->fgets();
var_dump($file->key());
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用PHP_INT_MAX
in SplFileObject::seek
方法.
$file = new \SplFileObject('file.extension', 'r');
$file->seek(PHP_INT_MAX);
echo $file->key() + 1;
Run Code Online (Sandbox Code Playgroud)
Dav*_*man 34
如果您在Linux/Unix主机上运行它,最简单的解决方案是使用exec()
或类似运行命令wc -l $path
.只要确保你已经$path
首先进行了清理,以确保它不像"/ path/to/file; rm -rf /".
And*_*ham 28
我发现有一种更快的方法,不需要循环遍历整个文件
仅在*nix系统上,在Windows上可能有类似的方式......
$file = '/path/to/your.file';
//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));
Run Code Online (Sandbox Code Playgroud)
如果您使用的是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
Run Code Online (Sandbox Code Playgroud)
小智 5
这是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;
}
Run Code Online (Sandbox Code Playgroud)
如果您使用的是Linux,则可以执行以下操作:
number_of_lines = intval(trim(shell_exec("wc -l ".$file_name." | awk '{print $1}'")));
Run Code Online (Sandbox Code Playgroud)
如果您使用的是其他操作系统,则只需找到正确的命令
问候
归档时间: |
|
查看次数: |
141920 次 |
最近记录: |