21CTO导读:在现在这篇文章中,我们将一起学习处理PHP中的大文件,避免因内存限制而无法使用的方法。
如果你想用PHP处理大文件,PHP提供了一些普通的PHP函数,比如file_get_contents()或file()函数,它们在处理超大文件时会存在一定局限性。
其中包括:
1.内存限制
上面这些函数依赖于php.ini中memory_limit的参数值设置,可以调整增加这些值,但这些函数仍然不适合极大的文件,因为这些函数会把整个文件内容都读入到内存中。
如果文件的尺寸超过memory_limit的设置,这类文件都不会加载到内存中。现实中,比如我们有一个20G的的文件,想用PHP来处理,该怎么样处理?
2.不太好的用户体验
还有一个限制是生产环境的速度问题。如果我们把它输出到数组中,这样经常会出现在浏览器出现很长时间的等待,白页或者出错等情况。这样给用户的体验不太好。
对于这种技术限制,可以使用yield关键字来直接生成一个结果。
SplFileObject Class
在本文中,我们告诉大家使用PHP标准库中的SplFileObject类。
我们来演示,创建一个类来处理大文件。
这个类使用文件名做为输入参数。如下代码:
class BigFile
{
protected $file;
public function __construct($filename, $mode = 'r')
{
if (!file_exists($filename)) {
throw new Exception('File not found');
}
$this->file = new SplFileObject($filename, $mode);
}
}
接下来,我们定义一个遍历文件的方法,这个方法将使用fgets()函数一次读取文件一行。
我们也可以使用fread()函数的方法。
读取文本文件
fgets()适用于解析包含换行符的文本文件,而fread()适用于解析二进制文件。
该函数用于遍历文本文件的每一行内容。
protected function iterateText()
{
$count = 0;
while (!$this->file->eof()) {
yield $this->file->fgets();
$count++;
}
return $count;
}
读取二进制文件
来看另一个读取二进制的文件:
protected function iterateBinary($bytes)
{
$count = 0;
while (!$this->file->eof()) {
yield $this->file->fread($bytes);
$count++;
}
}
直接读取
现在我们将定义一个采用迭代的类型,返回NoRewindIterator实例的方法。
我们使用NoRewindIterator强制单向读取。
public function iterate($type = 'Text', $bytes = NULL)
{
if ($type == 'Text') {
return new NoRewindIterator($this->iterateText());
} else {
return new NoRewindIterator($this->iterateBinary($bytes));
}
}
整个类源文件如下如示:
class BigFile
{
protected $file;
public function __construct($filename, $mode = 'r')
{
if (!file_exists($filename)) {
throw new Exception('File not found');
}
$this->file = new SplFileObject($filename, $mode);
}
protected function iterateText()
{
$count = 0;
while (!$this->file->eof()) {
yield $this->file->fgets();
$count++;
}
return $count;
}
protected function iterateBinary($bytes)
{
$count = 0;
while (!$this->file->eof()) {
yield $this->file->fread($bytes);
$count++;
}
}
public function iterate($type = 'Text', $bytes = NULL)
{
if ($type == 'Text') {
return new NoRewindIterator($this->iterateText());
} else {
return new NoRewindIterator($this->iterateBinary($bytes));
}
}
}
解析大文件:
我们对类文件做如下测试:
$largefile = new BigFile('file.csv');
$iterator = $largefile->iterate('Text'); // Text or Binary based on your file type
foreach ($iterator as $line) {
echo $line;
}
现在这个类可以读取任何尺寸的大文件,没有任何限制,无限大。
我们可以在Laravel项目中,将该类添加到composer.json文件中自动加载该类并直接使用。
从此,我们可以轻松使用PHP解析和处理大尺寸文件了。
Happy PHP Coding!
作者:飞花逐月
联系客服