【问题标题】:Problem with perl multiline matchingperl 多行匹配的问题
【发布时间】:2017-02-10 11:04:03
【问题描述】:

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

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

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

【问题讨论】:

  • 您的常见问题在第一句话中就得到了回答:“perldoc -q match” --> “我在匹配多行时遇到问题。怎么了?”
  • /s 只影响 . 匹配的内容,因此不需要您的 /ss

标签: regex perl


【解决方案1】:

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 对,.+ 的贪婪匹配将从第一个 START 到终点站。您可以对.+? 使用非贪婪匹配(尽可能少匹配)。

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

【讨论】:

  • -0 上也找不到任何信息。那个标志有什么作用?
  • 这让我快疯了!非常感谢:)
【解决方案2】:

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

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

【讨论】:

  • 那么-0777-00 是做什么的?我正在阅读 perl 联机帮助页,但除了那些数字是八进制(这很明显)之外,我找不到任何信息。谢谢!
  • 选项 -0 更改记录分隔符。 777 激活 slurp 模式,如果没有定义记录分隔符,s.t.一次读取整个文件。 0 将分隔符更改为空行。
【解决方案3】:

比较简单的单行(读取内存中的文件):

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

【讨论】:

    【解决方案4】:
    perl -MFile::Slurp -e '$content = read_file(shift); $content =~ s/START\s+STOP/REPLACE/s; print $content' input.txt
    

    【讨论】:

    • 为什么你会让人们使用非标准模块来完成一个简单的命令行就可以完全解决的问题?
    【解决方案5】:

    这是一个不会一次将整个文件读入内存的单行代码:

    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
    

    【讨论】:

    • 很好,虽然我认为 ARGV 没有做任何事情并且可以删除。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    相关资源
    最近更新 更多