我在处理大文件时没有经验,所以我不知道该怎么做.我试图使用file_get_contents读取几个大文件; 任务是使用preg_replace()清理和消除它们.
我的代码在小文件上运行良好; 但是,大文件(40 MB)会触发内存耗尽错误:
PHP Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 41390283 bytes)
Run Code Online (Sandbox Code Playgroud)
我正在考虑使用fread(),但我不确定它是否也能正常工作.这个问题有解决方法吗?
感谢您的输入.
这是我的代码:
<?php
error_reporting(E_ALL);
##get find() results and remove DOS carriage returns.
##The error is thrown on the next line for large files!
$myData = file_get_contents("tmp11");
$newData = str_replace("^M", "", $myData);
##cleanup Model-Manufacturer field.
$pattern = '/(Model-Manufacturer:)(\n)(\w+)/i';
$replacement = '$1$3';
$newData = preg_replace($pattern, $replacement, $newData);
##cleanup Test_Version field and create comma delimited layout.
$pattern = '/(Test_Version=)(\d).(\d).(\d)(\n+)/';
$replacement = '$1$2.$3.$4 ';
$newData = preg_replace($pattern, $replacement, $newData);
##cleanup occasional empty Model-Manufacturer field.
$pattern = '/(Test_Version=)(\d).(\d).(\d) (Test_Version=)/';
$replacement = '$1$2.$3.$4 Model-Manufacturer:N/A--$5';
$newData = preg_replace($pattern, $replacement, $newData);
##fix occasional Model-Manufacturer being incorrectly wrapped.
$newData = str_replace("--","\n",$newData);
##fix 'Binary file' message when find() utility cannot id file.
$pattern = '/(Binary file).*/';
$replacement = '';
$newData = preg_replace($pattern, $replacement, $newData);
$newData = removeEmptyLines($newData);
##replace colon with equal sign
$newData = str_replace("Model-Manufacturer:","Model-Manufacturer=",$newData);
##file stuff
$fh2 = fopen("tmp2","w");
fwrite($fh2, $newData);
fclose($fh2);
### Functions.
##Data cleanup
function removeEmptyLines($string)
{
return preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $string);
}
?>
Run Code Online (Sandbox Code Playgroud)
Rob*_*itt 88
首先,您应该了解当使用file_get_contents时,您将整个数据字符串提取到变量中,该变量存储在主机内存中.
如果该字符串大于PHP进程专用的大小,则PHP将暂停并显示上面的错误消息.
以此方式打开文件作为指针,然后一次取一个块,这样如果你有一个500MB的文件,你可以读取前1MB的数据,做你想做的,从中删除1MB系统的内存并替换为下一个MB,这使您可以管理放入内存的数据量.
如果可以在下面看到这个例子,我将创建一个类似于node.js的函数
function file_get_contents_chunked($file,$chunk_size,$callback)
{
try
{
$handle = fopen($file, "r");
$i = 0;
while (!feof($handle))
{
call_user_func_array($callback,array(fread($handle,$chunk_size),&$handle,$i));
$i++;
}
fclose($handle);
}
catch(Exception $e)
{
trigger_error("file_get_contents_chunked::" . $e->getMessage(),E_USER_NOTICE);
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
然后像这样使用:
$success = file_get_contents_chunked("my/large/file",4096,function($chunk,&$handle,$iteration){
/*
* Do what you will with the {$chunk} here
* {$handle} is passed in case you want to seek
** to different parts of the file
* {$iteration} is the section of the file that has been read so
* ($i * 4096) is your current offset within the file.
*/
});
if(!$success)
{
//It Failed
}
Run Code Online (Sandbox Code Playgroud)
您将发现的一个问题是,您尝试在极大数据块上执行多次正则表达式,不仅如此,而且您的正则表达式是为匹配整个文件而构建的.
使用上面的方法你的正则表达式可能变得无用,因为你可能只匹配一组半数据,你应该做的是恢复到本机字符串函数,如
strpossubstrtrimexplode匹配字符串,我已经在回调中添加支持,使把手和当前迭代传递,这将让你与文件直接在回调中工作,让您在使用类似功能fseek,ftruncate并fwrite为实例.
您构建字符串操作的方式无论如何都没有效率,并且使用上面提出的方法是一种更好的方法.
希望这可以帮助.
一个非常丑陋的解决方案,可以根据文件大小调整内存限制:
$filename = "yourfile.txt";
ini_set ('memory_limit', filesize ($filename) + 4000000);
$contents = file_get_contents ($filename);
Run Code Online (Sandbox Code Playgroud)
正确的解决方案是考虑是否可以以较小的块处理文件,或者使用PHP中的命令行工具。
如果您的文件是基于行的,您还可以使用fgets它逐行处理它。
| 归档时间: |
|
| 查看次数: |
46890 次 |
| 最近记录: |