Heim > Artikel > Backend-Entwicklung > PHP 大文件的读取和写入问题
在通常的学习和开发中,因为我们很少会接触到大量数据的读取和写入,所以当突然有了这种需求的时候,我们可
能仍然会按照一些比较快捷的方法,像file_get_contents,fread等方法来读取文件,不过这样以来如果读取的文件太
大,就会产生问题,在实现大文件读取和写入的时候查找了网上的一些资料,不过有些例子给的不是很符合我的需
求,所以我就结合网上已有的例子,再写一篇总结性的博客吧。
那么究竟会产生什么问题呢,这就要说一些PHP的底层实现数,file_get_contents和fread来说一下,首先说一下
PHP中文件读取的函数,file_get_contents和fread,这两个函数其实原理是相同的,都是读取内容到系统的内存中,
不过如果只是想将一个文件的内容读入到一个字符串中,请使用file_get_contents(),它的性能比 fread() 好得多。
在读取不是很大的文件还是没有问题的,不过当读取大文件的时候(例如2GB的日志),如果你机器的内存只有4G,
如果你将整个文件全部读取然后存入字符串中,就可能会导致系统的内存爆掉造成卡死,因为还有一部分内存要用于
维持系统的运行和其他进程的运行,既然会这样,我们就需要一些其他办法来避免一次性读取太多的内容,通过这种
办法来实现大文件的读取。
PHP文件读取:
下面这个是网上读取大文件的一个例子,来说明上面内存会爆掉的现象。。
_________________________________无敌分割线_______________________________________
我机器是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); }
大文件写入:
大文件的写入相对大文件读取来说产生的代价不是很大,因为文件写入是写入到硬盘中,如果一次性写入文件过多,
只会产生卡硬盘的现象,如果从效率上来讲一次性直接写入的耗时和效率最高,所以大文件的写入的话建议一次性读取后直接写入文件中。