【问题标题】:perl multiline find and replaceperl 多行查找和替换
【发布时间】:2012-01-20 02:37:44
【问题描述】:

我正在尝试对以下输入使用简单的 perl 单行:

@F7##########0/1
C4CTA6GCAAC56G67CTCA99C
+
b[[WZ56W]87X9HBB
@44FC6%%%%&&&&&&&1UP1
GTS4HY2IOMD3FCCA8DFLLLTG
+
]]^4YY23ZV\6`a8`^9^a

等等

我希望我的输出看起来像:

@F7##########0/1
C4CTA6GCAAC56G67CTCA99C
+F7##########0/1
b[[WZ56W]87X9HBB
@44FC6%%%%&&&&&&&1UP1
GTS4HY2IOMD3FCCA8DFLLLTG
+44FC6%%%%&&&&&&&1UP1
]]^4YY23ZV\6`a8`^9^a

等等

我想搜索以@ 开头的行,并将该行的其余部分存储(分组)在$1 中。然后我找到下一个出现的+ 并将$1 添加到该行的末尾。

我尝试过perl -pi -e "s%^@(.*)$\1\n(.*)$\2\n(\+)$\3\n%$1\n$2\n\+$1%mg" file.txt,但我似乎无法匹配^@(.*)$\1\n 之后的任何内容。

肯定有一个可行的单线来实现这一点。欢迎使用AwkSedtr 单行,但必须对file.txt 进行内联更改,因为file.txt 很大并且不希望写入另一个文件。

【问题讨论】:

    标签: regex perl sed awk multiline


    【解决方案1】:

    很遗憾,awk 不提供在线更改,因此它可能不是您需要的。但是,如果您这样做,那么以下将起作用-

    awk '/^@/{a=substr($0,2)}/^\+/{printf ("%s%s\n", $0,a);next}1' file > newfile
    

    更新:我已尝试在 sed 中执行您想要的操作,它允许更改 in-file

    sed -i '/^@/{h};/^\+/{x;s/\(.\)\(.*\)/+\2/}' file
    

    说明:

    • /^@/{h} :我们查找以@ 符号开头的行,当我们找到它时,我们将整行放入hold spaceSed 有两个缓冲区,pattern spacehold spacePattern space 是所有操作发生的地方。 hold space 允许我们暂时保留信息,以便我们以后可以对其进行一些操作。
    • /^\+/{x;... :当我们找到以+ 开头的行时,我们对其执行x 操作。这意味着,我们从hold space 中提取信息并将其放回pattern space。一旦我们这样做了,我们就做一个简单的替换。
    • ...s/\(.\)\(.*\)/+\2/ :这意味着我们使用grouping 来识别字符。由于我们的文本部分前面有@,这是您不想要的,我们使用. 隔离该字符,这意味着任何字符。我们还将该系列的其他所有内容放在第二组中。这些组需要转义{所以你会看到 \(\) 而不仅仅是 ()}。在替换部分,我们放入了+ 和第二组。请记住,捕获的第一组只有@。我们只想要第二组,所以我们使用\2(反斜杠和您希望引用的组数)来引用它。

    测试awk:

    [jaypal:~/Temp] cat file
    @F7##########0/1
    C4CTA6GCAAC56G67CTCA99C
    +
    b[[WZ56W]87X9HBB
    @44FC6%%%%&&&&&&&1UP1
    GTS4HY2IOMD3FCCA8DFLLLTG
    +
    ]]^4YY23ZV\6`a8`^9^a
    
    [jaypal:~/Temp] awk '/^@/{a=substr($0,2)}/^\+/{printf ("%s%s\n", $0,a);next}1' file
    @F7##########0/1
    C4CTA6GCAAC56G67CTCA99C
    +F7##########0/1
    b[[WZ56W]87X9HBB
    @44FC6%%%%&&&&&&&1UP1
    GTS4HY2IOMD3FCCA8DFLLLTG
    +44FC6%%%%&&&&&&&1UP1
    ]]^4YY23ZV\6`a8`^9^a
    

    测试sed:

    您可以使用-i 选项进行更改。以下仅为演示,以便您查看输出。

    [jaypal:~/Temp] sed '/^@/{h};/^\+/{x;s/\(.\)\(.*\)/+\2/}' file
    @F7##########0/1
    C4CTA6GCAAC56G67CTCA99C
    +F7##########0/1
    b[[WZ56W]87X9HBB
    @44FC6%%%%&&&&&&&1UP1
    GTS4HY2IOMD3FCCA8DFLLLTG
    +44FC6%%%%&&&&&&&1UP1
    ]]^4YY23ZV\6`a8`^9^a
    

    【讨论】:

    • sed 的那一行正是我在尝试使用 sed 时最初寻找的内容。但是,当我无法匹配或替换\n 时,我放弃了sed。它看起来像复杂的正则表达式,您介意为我解释一下吗?
    • 这真是太棒了!也是一个 A+ 的解释。谢谢!
    • 干得好。在 sed 解决方案中,/^\+/ 让我愣了一下,因为没有-r 开关+ 并不特殊,通常不需要转义。但是,如果它被转义,则意味着 1 or more of the preceeding character or subexpression 但在不正当的逻辑中,因为 ^ 是位置标记而不是字符,它会恢复为 + 字符。
    【解决方案2】:

    我很抱歉。我更仔细地阅读了你的问题,发现你想逐行处理你的文件。这条单线将实现这一目标

    perl -pe "$dat = $1 if /^\@(.+)/; s/^\+/+$dat/;" infile
    

    【讨论】:

      【解决方案3】:

      下面的程序似乎可以满足您的需要

      use strict;
      use warnings;
      
      my $str = <<'STR';
      @F7##########0/1
      C4CTA6GCAAC56G67CTCA99C
      +
      b[[WZ56W]87X9HBB
      @44FC6%%%%&&&&&&&1UP1
      GTS4HY2IOMD3FCCA8DFLLLTG
      +
      ]]^4YY23ZV\6`a8`^9^a
      STR
      
      $str =~ s/^@(.+?)$(.+?)^\+/\@$1$2+$1/gms;
      
      print $str;
      

      输出

      @F7##########0/1
      C4CTA6GCAAC56G67CTCA99C
      +F7##########0/1
      b[[WZ56W]87X9HBB
      @44FC6%%%%&&&&&&&1UP1
      GTS4HY2IOMD3FCCA8DFLLLTG
      +44FC6%%%%&&&&&&&1UP1
      ]]^4YY23ZV\6`a8`^9^a
      

      【讨论】:

        【解决方案4】:

        这可能对你有用:

        sed '/^@/h;/^+/{G;s/\n@//}' file
        @F7##########0/1
        C4CTA6GCAAC56G67CTCA99C
        +F7##########0/1
        b[[WZ56W]87X9HBB
        @44FC6%%%%&&&&&&&1UP1
        GTS4HY2IOMD3FCCA8DFLLLTG
        +44FC6%%%%&&&&&&&1UP1
        ]]^4YY23ZV\6`a8`^9^a
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-22
          • 1970-01-01
          • 2021-01-31
          • 1970-01-01
          相关资源
          最近更新 更多