2019独角兽企业急聘Python工程师标准>>>
在php中,读取文件时,最快的方式就是使用file、file_get_contents等一些函数。 简单的几行代码就可以漂亮的完成我们需要的功能。 但是,当所操作的文件是比较大的文件时,这些功能可能就不够用了。 下面将从一个需求开始说明读取大文件的常用操作方法。
需要
有一个800M的日志文件,大约有500万行。 使用php返回最后几行的内容。
实现方法
1.简单粗暴php读取php内容,只需使用文件功能直接操作
注意:由于file函数一次性将所有内容读入显存,而php避免一些写得不好的程序占用过多显存而造成系统显存不足,导致服务器宕机,所以默认限制为仅显存最大使用量为16M,通过php.ini中的memory_limit = 16M来设置,如果该值设置为-1,则显存使用量不受限制。
下面是一段使用file提取文件最后一行的代码。
ini_set('memory_limit','-1');
$file = 'access.log';
$data = file($file);
$line = $data[count($data)-1];
echo $line;
完成整个代码的执行花费了116.9613(s)。 很容易导致内存耗尽而导致设备死机或无响应
2.直接调用linux的tail命令显示最后几行
在Linux命令行下,可以直接使用tail -n 10 access.log轻松显示日志文件的最后几行。 可以直接使用php调用tail命令,执行php代码如下。
file = 'access.log';
$file = escapeshellarg($file); // 对命令行参数进行安全转义
$line = `tail -n 1 $file`;
echo $line
整个代码执行时间为0.0034(s)
3.直接使用php的fseek进行文件操作
这种方法是最常用的方法。 它不需要将文件的所有内容读入content中,而是直接通过指针进行操作,因此效率相当高效。 使用fseek对文件进行操作时,也有很多区别。 的方式,效率也可能略有不同,以下是常用的两种方式。
方法一
首先通过fseek找到文件的最后一个EOF,然后找到最后一行的起始位置,得到这一行的数据,然后找到下一行的起始位置,然后取这一行的位置,以此类推php读取php内容,直到找到 $num 为止。
实现代码如下
$fp = fopen($file, "r");
$line = 10;
$pos = -2;
$t = " ";
$data = "";
while ($line > 0) {
while ($t != "n") {
fseek($fp, $pos, SEEK_END);
$t = fgetc($fp);
$pos --;
}
$t = " ";
$data .= fgets($fp);
$line --;
}
fclose ($fp);
echo $data
整个代码执行耗时0.0095(s)
方法二
仍然采用fseek的形式从文件末尾开始读取,不过此时并不是一个一个的读取,而是一个一个的读取。 读取一条数据时,将读取到的数据放入一个buf中,然后通过换行符(n)的个数来判断最后$num行数据是否已经读取完毕。
实现代码如下
$fp = fopen($file, "r");
$num = 10;
$chunk = 4096;
$fs = sprintf("%u", filesize($file));
$max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file);
for ($len = 0; $len < $max; $len += $chunk) {
$seekSize = ($max - $len > $chunk) ? $chunk : $max - $len;
fseek($fp, ($len + $seekSize) * -1, SEEK_END);
$readData = fread($fp, $seekSize) . $readData;
if (substr_count($readData, "n") >= $num + 1) {
preg_match("!(.*?n){".($num)."}$!", $readData, $match);
$data = $match[0];
break;
}
}
fclose($fp);
echo $data;
整个代码执行需要 0.0009(s) 才能完成。
方法三
function tail($fp,$n,$base=5)
{
assert($n>0);
$pos = $n+1;
$lines = array();
while(count($lines)< =$n){
try{
fseek($fp,-$pos,SEEK_END);
} catch (Exception $e){
fseek(0);
break;
}
$pos *= $base;
while(!feof($fp)){
array_unshift($lines,fgets($fp));
}
}
return array_slice($lines,0,$n);
}
var_dump(tail(fopen("access.log","r+"),10));
整个代码执行完成需要 0.0003(s)
或者借助linux管道
$handle = popen("cat access.log 2>&1", 'r');
while(!feof($handle))
{
echo $line = fgets($handle);
}
pclose($handle);