什么';搜索文件内容并用PHP更改内容的最佳(最有效)方法是什么?

什么';搜索文件内容并用PHP更改内容的最佳(最有效)方法是什么?,php,file,io,Php,File,Io,我有一个用PHP读取的文件。我想寻找一些以空格开头的行,然后是我正在寻找的一些关键词(例如,“project_name:”),然后更改该行的其他部分 目前,我处理这个问题的方法是将整个文件读入一个字符串变量,操纵该字符串,然后将整个内容写回文件,完全替换整个文件(通过fopen(filepath,“wb”)和fwrite()),但这感觉效率低下。有更好的方法吗?更新:完成函数后,我有时间对其进行基准测试。我使用了一个1GB大文件进行测试,但结果不令人满意:| 是的,内存峰值分配明显更小: 标准

我有一个用PHP读取的文件。我想寻找一些以空格开头的行,然后是我正在寻找的一些关键词(例如,“project_name:”),然后更改该行的其他部分


目前,我处理这个问题的方法是将整个文件读入一个字符串变量,操纵该字符串,然后将整个内容写回文件,完全替换整个文件(通过
fopen(filepath,“wb”)
fwrite()
),但这感觉效率低下。有更好的方法吗?

更新:完成函数后,我有时间对其进行基准测试。我使用了一个
1GB
大文件进行测试,但结果不令人满意:|

是的,内存峰值分配明显更小:

  • 标准溶液:1,86 GB
  • 自定义解决方案:653 KB(4096字节缓冲区大小)
但与以下解决方案相比,性能略有提升:

ini_set('memory_limit', -1);

file_put_contents(
    'test.txt',
    str_replace('the', 'teh', file_get_contents('test.txt'))
);
上面的脚本耗时约16秒,自定义解决方案耗时约13秒

简历:custome解决方案在处理大文件时速度稍快,占用的内存也少得多(!!!)

另外,如果您希望在web服务器环境中运行此功能,那么定制解决方案会更好,因为许多并发脚本可能会消耗整个系统的可用内存


原始答案:

记住的唯一一件事是,按照文件系统块的大小分块读取文件,然后将内容或修改的内容写回临时文件。完成处理后,使用
rename()
覆盖原始文件

这将减少内存峰值,如果文件确实很大,则速度应该会明显加快

注意:在linux系统上,可以使用以下方法获得文件系统块大小:

sudo dumpe2fs /dev/yourdev | grep 'Block size'
我得到了
4096

功能如下:

function freplace($search, $replace, $filename, $buffersize = 4096) {

    $fd1 = fopen($filename, 'r');
    if(!is_resource($fd1)) {
        die('error opening file');
    }   

    // the tempfile can be anywhere but on the same partition as the original
    $tmpfile = tempnam('.', uniqid());
    $fd2 = fopen($tmpfile, 'w+');

    // we store len(search) -1 chars from the end of the buffer on each loop
    // this is the maximum chars of the search string that can be on the 
    // border between two buffers
    $tmp = ''; 
    while(!feof($fd1)) {
        $buffer = fread($fd1, $buffersize);
        // prepend the rest from last one
        $buffer = $tmp . $buffer;
        // replace
        $buffer = str_replace($search, $replace, $buffer);
        // store len(search) - 1 chars from the end of the buffer
        $tmp = substr($buffer, -1 * (strlen($search)) + 1); 
        // write processed buffer (minus rest)
        fwrite($fd2, $buffer, strlen($buffer) - strlen($tmp));
    };  

    if(!empty($tmp)) {
        fwrite($fd2, $tmp);
    }   

    fclose($fd1);   
    fclose($fd2);
    rename($tmpfile, $filename);
}
可以这样称呼:

freplace('foo', 'bar', 'test.txt');

“最好”是主观的。从“接近”的理由来看,“我们期望答案是由事实、参考文献或特定的专门知识所支持的,但是这个问题可能会引起辩论、争论、轮询或扩展讨论。”你会考虑通过选择一个特定的方法来解释你的问题,并解释它如何不符合你的需要吗?这将允许is提供具体而非主观的答案。@GeorgeCummins您的评论在这里不适用。这是一个典型的编程question@Baba你确定你的尝试比那次快吗?请注意,简单的
rename()
非常快。将准备一些基准测试:)还请注意,在大多数应用程序中,不知道应该在哪里替换字符串scenarios@hek2mgl我想你应该做你的测试。。。。你的功能有很多问题。。这使速度慢了10倍。。。而且expensive@Baba我仍然不明白这是怎么一个重复,你的答案如何适合这里。您有一个位置,文本应该被替换为函数的参数。请注意,此处不知道位置。它是搜索和替换,而不是注入。你能告诉我我的答案有哪些错误吗?我想对这两种解决方案进行基准测试(也许会改进),但我无法对它们进行比较,因为它们并不相同。我知道你是一个聪明的人,也许我遗漏了一些东西,如果搜索字符串跨读缓冲区会发生什么?谢谢你的评论。我已经更新了帖子。是的,要想获得这种防弹效果,需要更多的关注。@hek2mgl这样做是否有好处,而不是用“x+”开头,然后结合这些步骤?是的。好处是降低了内存峰值。您只需要~$buffersize内存,而不是文件大小。不过,我会在有时间的时候更新帖子。也许在晚上。将准备一个处理Jon评论的版本。。差不多准备好了:)@Jon已经更新了帖子来处理你的评论。谢谢你。当然,这不适用于正则表达式