在上一篇文章中,针对查询数据表时候,php出现 “Allowed memory size of xxx bytes”的错误,在不修改php.ini配置的时候,用另外的函数来解决了这个问题,以牺牲部分性能的代价来换取内存空间,在这篇文章中,会说一下在读取文件的时候出现这个问题的解决思路。
在读取文件的时候,file_get_contents()和file()是大家惯用的两个函数,因为操作会比较少一点,我这个地方下载了一个200M的日志文件作为操作对象。
// 这里是一般的写法
//$file = file_get_contents($log_file);
$file = file($log_file);
$start = memory_get_usage();
$end = memory_get_usage();
$used = $end - $start;
echo $used;
无论file_get_contents还是file,都和预料中一样,出现了错误”Fatal error: Allowed memory size of 134217728 bytes exhausted”。
现在我们开始看另外几种写法
代码1:
这是采用fgets()每次读取一行的结果,但是如果一行比较大的情况下,也会出现内存不够用的错误提示
function test1($log_file) {
// 内存:664byte
// 时间:13.063009977341秒
$fp = fopen($log_file, 'r+');
while(! feof($fp)) {
$row = fgets($fp);
// $row = fread($fp, 2048);
echo $row, "\r\n";
}
fclose($fp);
}
代码2:
这里采用了用fread()函数来控制每次读取的数据大小,这个内存占用和耗时,跟设置的每次读取的数据大小有直接关系
function test3($log_file) {
// 内存:656byte
// 时间:9.2053289413452秒
$fp = fopen($log_file, 'r+');
while(! feof($fp)) {
$row = fread($fp, 2048);
//echo $row;
}
fclose($fp);
}
代码3:
这种方式是用过用管道的方式打开文件,和代码1对比,在同使用fgets()的情况下,内存要比代码1中占用少一些。
function test2($log_file) {
// 内存:352byte
// 时间:13.38470697403秒
$cmd = "cat {$log_file} 2>&1";
$fp = popen($cmd, "r");
while(!feof($fp))
{
$row = fgets($fp);
echo $row, "\r\n";
}
pclose($fp);
}
另外还有使用fseek()函数等等,但都是在达到同一个目的,就是使得只有处理的数据读取到内存中来减少内存占用。
同样也可以使用php调用系统命令方式来达到目的,这里没有对那种方案进行测试。
附上我测试使用的函数:
$start = memory_get_usage();
$time1 = microtime(true);
test3($log_file);
$end = memory_get_usage();
$time2 = microtime(true);
$used = $end - $start;
$limit = $time2 - $time1;
echo "内存:", $used, "byte\r\n时间:", $limit, "秒\r\n";