【问题标题】:string replace in a large file with php用php替换大文件中的字符串
【发布时间】:2011-01-10 16:06:49
【问题描述】:

我正在尝试对 PHP 中的整个文件进行字符串替换。我的文件超过 100MB,所以我必须逐行进行,不能使用file_get_contents()。有什么好的解决办法吗?

【问题讨论】:

  • 有必要使用PHP吗?如果您可以访问命令行,则可以使用sed 命令来执行相同的功能,这可能不会那么令人头疼。如果需要自动化,可以制作一个在 cron 上运行的 shell 脚本。
  • 同意,这是命令行的东西,不是 PHP。

标签: php string replace readfile


【解决方案1】:

如果您不需要使用 PHP,我强烈建议您从命令行执行类似的操作。它是迄今为止最适合这项工作的工具,而且更易于使用。

无论如何,sed (Stream Editor) 命令就是您要寻找的:

sed s/search/replace oldfilename > newfilename

如果您需要不区分大小写:

sed s/search/replace/i oldfilename > newfilename

如果你需要在 PHP 中动态执行,你可以使用passthru():

$output = passthru("sed s/$search/$replace $oldfilename > $newfilename");

【讨论】:

  • 为什么是passthru() 而不是exec()
  • 两者都可以,但使用 passthru,您可以获得整个输出。 exec 只返回输出的最后一行。
  • 用passthru调用sed没那么容易,详情见我的回答。
  • 不区分大小写:sed s/search/replace/i old_file > new_fileman sed
  • 我知道这太旧了.. 但是我可以尝试使用 sed 替换 Ø到 Ø "sed 's/Ø/Ø' {$this->xml->path} > {$this->xml->path}" 但我收到此错误 sh: 1: Oslash: not found sed: -e expression #1, char 2: unterminated `s' command sh: 1: /Ø: not found
【解决方案2】:

给你:

function replace_file($path, $string, $replace)
{
    set_time_limit(0);

    if (is_file($path) === true)
    {
        $file = fopen($path, 'r');
        $temp = tempnam('./', 'tmp');

        if (is_resource($file) === true)
        {
            while (feof($file) === false)
            {
                file_put_contents($temp, str_replace($string, $replace, fgets($file)), FILE_APPEND);
            }

            fclose($file);
        }

        unlink($path);
    }

    return rename($temp, $path);
}

这样称呼它:

replace_file('/path/to/fruits.txt', 'apples', 'oranges');

【讨论】:

  • 这是个笑话。我不知道为什么会很有趣。我将把它交给我们的 JDT(笑话开发团队),并在 6 到 8 周内回复您。
【解决方案3】:

如果您不能直接从命令行使用 sed,因为它是一项动态任务,并且您需要从 php 调用它,则很难获得正确的语法:您必须在搜索和替换字符串中以不同的方式转义这些字符

' / $ . * [ ] \ ^ &

以下函数搜索并替换文件中的字符串而不将搜索到的字符串解释为正则表达式。因此,如果您愿意,可以搜索字符串“.*”并将其替换为“$”。

/**
 * str_replace_with_sed($search, $replace, $file_in, $file_out=null)
 * 
 * Search for the fixed string `$search` inside the file `$file_in`
 * and replace it with `$replace`. The replace occurs in-place unless
 * `$file_out` is defined: in that case the resulting file is written
 * into `$file_out`
 *
 * Return: sed return status (0 means success, any other integer failure)
 */
function str_replace_with_sed($search, $replace, $file_in, $file_out=null)
{
    $cmd_opts = '';
    if (! $file_out) 
    {
        // replace inline in $file_in
        $cmd_opts .= ' -i';
    }

    // We will use Basic Regular Expressions (BRE). This means that in the 
    // search pattern we must escape
    // $.*[\]^
    //
    // The replacement string must have these characters escaped
    // \ & 
    //
    // In both cases we must escape the separator character too ( usually / )
    // 
    // Since we run the command trough the shell we We must escape the string
    // too (yai!). We're delimiting the string with single quotes (') and we'll
    // escape them with '\'' (close string, write a single quote, reopen string)    

    // Replace all the backslashes as first thing. If we do it in the following
    // batch replace we would end up with bogus results
    $search_pattern = str_replace('\\', '\\\\', $search);

    $search_pattern = str_replace(array('$', '.', '*', '[', ']', '^'),
                                  array('\\$', '\\.', '\\*', '\\[', '\\]', '\\^'),
                                  $search_pattern);

    $replace_string = str_replace(array('\\', '&'),
                                  array('\\\\', '\\&'),
                                  $replace);

    $output_suffix = $file_out ? " > '$file_out' " : '';
    $cmd = sprintf("sed ".$cmd_opts." -e 's/%s/%s/g' \"%s\" ".$output_suffix,
                    str_replace('/','\\/', # escape the regexp separator
                      str_replace("'", "'\''", $search_pattern) // sh string escape
                    ),
                    str_replace('/','\\/', # escape the regexp separator
                      str_replace("'", "'\''", $replace_string) // sh string escape
                    ),
                    $file_in
                  );

    passthru($cmd, $status);

    return $status;
}

【讨论】:

    【解决方案4】:

    我会以更明确的方式使用“sed”,这样您就可以减少对系统的依赖。

    $output = passthru("sed -e 's/$search/$replace/g' $oldfilename > $newfilename");
    

    【讨论】:

      【解决方案5】:

      一次获取几行,转储变量,获取接下来的几行。

      $fh = fopen("bigfile.txt", "flags");
      $num = 0;
      $length = 300;
      $filesize = filesize("bigfile.txt");
      
      while($num < $filesize)
      {
           $contents = fread($fh, $length);
           // .. do stuff ...
           $num = $num+$length;
           fseek($fh, $num);
      }
      
      fclose($fh);
      

      您需要确保它是正确的(尚未测试)。请参阅 PHP Documentation. 上的库

      棘手的部分是写回文件。我脑海中浮现的第一个想法是进行字符串替换,将新内容写入另一个文件,然后在最后删除旧文件并用新文件替换。

      【讨论】:

      • 是的,没错……一个穷人的流媒体。如果您在命令行上执行此操作,它将有效地工作:cat file | sed 's/replace/something/g' > output.file
      • 我会运行一个调用命令行函数的 php 脚本——然后打印输出文件:)
      • 如果您要替换的字符串长于单个字符,则此方法会出现问题。字符串很可能跨越多个数据块,导致您跳过替换。
      【解决方案6】:

      这样的?

      $infile="file";
      $outfile="temp";
      $f = fopen($infile,"r");
      $o = fopen($outfile,"a");
      $pattern="pattern";
      $replace="replace";
      if($f){
           while( !feof($f) ){
              $line = fgets($f,4096);
              if ( strpos($pattern,"$line") !==FALSE ){
                  $line=str_replace($pattern,$replace,$line);
              }
              fwrite($o,$line);
           }
      }
      fclose($f);
      fclose($o);
      rename($outfile,$infile);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-07
        • 1970-01-01
        • 2018-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-04
        相关资源
        最近更新 更多