在 PHP 文件读取过程中,如何尽量减少内存消耗

829 阅读1分钟
在 PHP 中,有多种简单的方式实现文件读取:file(),file_get_contents(),readfile()…… 


不同的实现方法,对内存的消耗不同。例如,file() 将整个文件内容读入一个数组,file_get_contents() 将整个文件读入一个字符串,此时内存的消耗取决于文件的大小。


通过对以下三种读取文件方式在内存消耗方面的比较,以后在遇到类似的问题时,可以更合理的选择处理方式。


⒈ 传统的按行读取文件

// common.php

/**
* 公共方法,用于文件单位之间的转换
*
* @param int $bytes 以字节计的文件大小
* @param int $precision 精度(经单位转换后保留的小数位数)
*/
function getReadableSize(int $bytes, int $precision = 2)
{
    $units = ['b', 'k', 'm', 'g', 't'];

    $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];
}

require 'common.php';

$file = $path . '/shakespeare.txt';

if (!file_exists($file)) {
    return false;
}

$fp = fopen($file, 'r');

$lines = [];
while(!feof($fp)) {
    $lines[] = fgets($fp);
}
fclose($fp);
return $lines;

echo '文件大小:' . getReadableSize(filesize($file)) . PHP_EOL;
echo '内存使用:' . getReadableSize(memory_get_peak_usage()) . PHP_EOL;

经测试,使用这种方式读取文件,文件本身大小为 5.2M,最大内存消耗量为 13M。


⒉ 使用生成器读取文件内容

require 'common.php';

function readFileWithYield(string $file)
{
    $fp = fopen($file, 'r');
    while(!feof($fp)) {
        yield fgets($fp);
    }
    fclose($fp);
}

$file = $path . '/shakespeare.txt';

if (!file_exists($file)) {
    return false;
}

$str = '';
$i = 0;
foreach(readFileWithYield($file) as $line) {
    preg_match("/\n{3}/", $str, $matches);

   if (count($matches)) {
       $i ++;
       $str = '';
   } else {
       $str .= $line . PHP_EOL;
   }
}

echo $i . PHP_EOL;
echo '文件大小:' . getReadableSize(filesize($file)) . PHP_EOL;
echo '内存使用:' . getReadableSize(memory_get_peak_usage()) . PHP_EOL;

经测试,使用这种方式读取文件,文件本身大小为 5.2M,最大内存消耗量为 433K。


⒊ 使用流传输的方式读取文件

require 'common.php';

$file = $path . '/shakespeare.txt';

if (!file_exists($file)) {
    return false;
}

$fp = fopen($file, 'r');
$fp_copy = fopen('./copy.txt', 'w');

stream_copy_to_stream($fp, $fp_copy);

fclose($fp);
fclose($fp_copy);

echo '文件大小:' . getReadableSize(filesize($file)) . PHP_EOL;
echo '内存使用:' . getReadableSize(memory_get_peak_usage()) . PHP_EOL;

经测试,使用这种方式读取文件,文件本身大小为 5.2M,最大内存消耗量为 430K。