在通常的学习和开发中,因为我们很少会接触到大量数据的读取和写入,所以当突然有了这种需求的时候,我们可
能仍然会按照一些比较快捷的方法,像file_get_contents,fread等方法来读取文件,不过这样以来如果读取的文件太
大,就会产生问题,在实现大文件读取和写入的时候查找了网上的一些资料,不过有些例子给的不是很符合我的需
求,所以我就结合网上已有的例子,再写一篇总结性的博客吧。
那么究竟会产生什么问题呢,这就要说一些PHP的底层实现数,file_get_contents和fread来说一下,首先说一下
PHP中文件读取的函数,file_get_contents和fread,这两个函数其实原理是相同的,都是读取内容到系统的内存中,
不过如果只是想将一个文件的内容读入到一个字符串中,请使用file_get_contents(),它的性能比 fread() 好得多。
在读取不是很大的文件还是没有问题的,不过当读取大文件的时候(例如2GB的日志),如果你机器的内存只有4G,
如果你将整个文件全部读取然后存入字符串中,就可能会导致系统的内存爆掉造成卡死,因为还有一部分内存要用于
维持系统的运行和其他进程的运行,既然会这样,我们就需要一些其他办法来避免一次性读取太多的内容,通过这种
办法来实现大文件的读取。
PHP文件读取:
下面这个是网上读取大文件的一个例子,来说明上面内存会爆掉的现象。。
_________________________________无敌分割线_______________________________________
需求如下: 现有一个1G左右的日志文件,大约有500多万行, 用php返回最后几行的内容。
实现方法:
1. 直接采用file函数来操作
注:由于 file函数是一次性将所有内容读入内存,而php为了防止一些写的比较糟糕的程序占用太多的内存而导致系统内存不足,使服务器出现宕机,所以默认情况下限制只能最大使用内存16M,这是通过php.ini里的memory_limit = 16M来进行设置,这个值如果设置-1,则内存使用量不受限制.
下面是一段用file来取出这具文件最后一行的代码.
整个代码执行完成耗时 116.9613 (s).
我机器是2个G的内存,当按下F5运行时,系统直接变灰,差不多20分钟后才恢复过来,可见将这么大的文件全部直接读入内存,后果是多少严重,所以不在万不得以,memory_limit这东西不能调得太高,否则只有打电话给机房,让reset机器了.
上面的例子虽然是读取最后几行的例子,不过由于对文件的内容进行了遍历,所以跟读取整个文件是一样的,不过如
果是为了读取最后几行的内容,也可以直接用fseek来进行定位读取部分内容。
下面我们来一起探讨下如何对大文件进行读取和写入。
大文件读取:
因为要求读取部分,如果文件不是特别大,可以通过file_get_contents或fread自带的分割参数来进行分块来进行读取(这个地方感觉需要加一个sleep函数,来减少IO的峰值大小,不过不知是否正确,希望大牛指点),还有一种方法,就是通过while循环,用fgets来进行逐行的读取,因为fgetss是通过文件指针读取一行,效率是比较高的。
下面附上通过fgets实现的大文件读取并对文件内容进行编码转换(UTF-8 -> GBK)的例子,代码如下:
$file = fopen($old_file_path,"r");
$result = fopen($temporary_file_path,"a");
$re_sign = 0;
while(!feof($file))
{
$content = fgets($file);
$encode = mb_detect_encoding($content, array('ASCII','UTF-8','GB2312','GBK','BIG5'));
if ($encode == 'UTF-8') {
$str = iconv($encode,"GBK//IGNORE", $content);
$encode = mb_detect_encoding($content, array('ASCII','UTF-8','GB2312','GBK','BIG5'));
fwrite($result, $str);
$re_sign = 1;
} else {
fwrite($result, $content);
}
}
fclose($file);
fclose($result);
if($re_sign == 1){
rename($old_file_path, $old_file_path . '.bak' );
rename($temporary_file_path, $old_file_path);
} else {
unlink($temporary_file_path);
}
大文件写入:
大文件的写入相对大文件读取来说产生的代价不是很大,因为文件写入是写入到硬盘中,如果一次性写入文件过多,
只会产生卡硬盘的现象,如果从效率上来讲一次性直接写入的耗时和效率最高,所以大文件的写入的话建议一次性读取后直接写入文件中。