perl多行匹配问题

时间:2011-05-02 21:04:57

标签: regex perl

我正在尝试使用perl one-liner更新一些跨越多行的代码并且看到一些奇怪的行为。这是一个简单的文本文件,显示我看到的问题:

ABCD    START
         STOP    EFGH

我希望以下内容能够正常运行,但最终不会取代任何内容:

perl -pi -e 's/START\s+STOP/REPLACE/s' input.txt

在做了一些实验之后,我发现原始正则表达式中的\s+将匹配换行符,但不会匹配第二行中的任何空格,并且添加第二行\s+也不起作用。所以现在我正在做以下的解决方法,即添加一个仅删除换行符的中间正则表达式:

perl -pi -e 's/START\s+/START/s' input.txt

这将创建以下中间文件:

ABCD    START            STOP    EFGH

然后我可以运行原始正则表达式(尽管不再需要/s):

perl -pi -e 's/START\s+STOP/REPLACE/s' input.txt

这会创建最终的所需文件:

ABCD    REPLACE    EFGH

似乎不需要中间步骤。我错过了什么吗?

5 个答案:

答案 0 :(得分:23)

perl -p一次处理一行文件。你拥有的正则表达式是正确的,但它永远不会与多行字符串匹配。

假设文件适合内存,一个简单的策略是读取整个内容(不使用-p执行此操作):

$/ = undef;
$file = <>;
$file =~ s/START\s+STOP/REPLACE/sg;
print $file;

注意,我添加了/g修饰符以指定全局替换。

作为所有额外样板的快捷方式,您可以将现有脚本与-0777选项一起使用:perl -0777pi -e 's/START\s+STOP/REPLACE/sg'。如果您可能需要在文件中进行多次替换,则仍需要添加/g

你可能遇到的打嗝,虽然不是这个正则表达式:如果正则表达式是START.+STOP,并且一个文件包含多个START / STOP对,.+的贪婪匹配会吃掉所有的首先开始到最后一次停止。您可以使用非贪婪匹配(尽可能少匹配)与.+?

如果您想在字符串中的任何位置使用^$锚点作为行边界,那么您还需要/m正则表达式修饰符。

答案 1 :(得分:21)

你很亲密。您需要-00-0777

 perl -0777 -pi -e 's/START\s+/START/' input.txt

答案 2 :(得分:5)

一个相对简单的单线程(读取内存中的文件):

perl -pi -e 'BEGIN{undef $/;} s/START\s+STOP/REPLACE/sg;' input.txt

另一种选择(不是那么简单),而不是在内存中读取文件:

perl -ni -e '$a.=$_; \
             if ( $a =~ s/START\s+STOP/REPLACE/s ) { print $a; $a=""; } \
             END{$a && print $a}' input.txt

答案 3 :(得分:3)

这是一个单行程序,不会立即将整个文件读入内存:

perl -i -ne 'if (($x = $last . $_) =~ s/START\n\s*STOP/REPLACE/) \
  { print $x; $last = ""; } else { print $last; $last = $_; } \
  print $last if eof ARGV' input.txt

答案 4 :(得分:2)

perl -MFile::Slurp -e '$content = read_file(shift); $content =~ s/START\s+STOP/REPLACE/s; print $content' input.txt