用php读取大文件

一个国家强大了,别的国家都会来跟你建交;一个人强大了,别的人都会跟你友好,一个男人强大了,好女孩自然会来找你。所以,不要苦苦地等一个人,不要为无法赢得一个人的心而懊丧,应加强自身建设。

准备

PHP已经为我们提供了函数memory_get_peak_usage,用来返回脚本运行过程中分配给 PHP 内存的峰值.单位是字节, 我们需要写一个函数将这个结果转化为我们可以轻易理解的单位, 比如MB;
memory.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

function formatBytes($bytes, $precision = 2) {
$units = array("b", "kb", "mb", "gb", "tb");

$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);

$bytes /= (1 << (10 * $pow));

return round($bytes, $precision) . " " . $units[$pow];
}

print formatBytes(memory_get_peak_usage());

然后我们准备了一个存有59w个邮箱的txt文件, 文本大小13M;

使用fgets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

function readTheFile($path) {
$handle = fopen($path, "r");

while(!feof($handle)) {
echo trim(fgets($handle)).PHP_EOL;
}
fclose($handle);
}

readTheFile("59w.txt");

require "memory.php";

结果: 62.35 mb

使用生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

function readTheFile($path) {
$handle = fopen($path, "r");

while(!feof($handle)) {
yield trim(fgets($handle)).PHP_EOL;
}
fclose($handle);
}

$iterator = readTheFile("59w.txt");

foreach ($iterator as $iteration) {
echo $iteration;
}

require "memory.php";

结果: 382.88 kb

管道间的文件

在我们不需要处理数据的情况下,我们可以把文件数据传递到另一个文件。通常被称为管道(大概是因为我们看不到除了两端的管子里面,当然,它也是不透明的),我们可以通过使用流方法实现。让我们先写一个脚本从一个文件传到另一个文件。这样我们可以测量内存的占用情况:

1
2
3
4
5
6
7
<?php

file_put_contents(
"test1.txt", file_get_contents("59w.txt")
);

require "memory.php";

结果: 14.34 mb

让我们尝试用流(管道)来传送一个文件到另一个:

1
2
3
4
5
6
7
8
9
10
11
<?php

$handle1 = fopen("59w.txt", "r");
$handle2 = fopen("test2.txt", "w");

stream_copy_to_stream($handle1, $handle2);

fclose($handle1);
fclose($handle2);

require "memory.php";

结果: 381.77 kb

https://www.oschina.net/translate/performant-reading-big-files-php