perl:如何删除两个模式之间的特定单词或模式

perl: how to remove particular word or pattern in between two patterns

我想使用 perl 删除两个模式中的一些单词

以下是我的正文

..........

QWWK jhjh  kljdfh jklh jskdhf jkh PQXY
lhj ah jh sdlkjh PQXY jha slkdjh 
PQXY jh alkjh ljk
kjhaksj dkjhsd KWWQ
hahs dkj h PQXY
.........

现在我想删除仅位于两个模式之间的所有 PQXY 个单词 ^QWWKKWWQ$

我知道如何通过以下命令替换两个模式之间的整个内容

perl -0777pe 's/^QWWK(?:(?!QWWK|KWWQ).)*KWWQ$/sometext/gms' filename

另请注意,^QWWK(?:(?!QWWK|KWWQ).)*KWWQ$ 此模式仅匹配那些中间没有 QWWK 和 KWWQ 的模式。

您可以使用范围运算符:

perl -pe 's/PQXY//g if /^QWWK/ .. /KWWQ$/'

更新: 仅当 ^QWWK 和 KWWQ$ 之间不存在 QWWK 或 KWWQ 时才替换 PQXY 试试这个:

perl -pe 'if (/^QWWK/ .. /KWWQ$/) {s/PQXY//g if ! /.+QWWK/ && !/KWWQ.+/}' filename

我确定它可以清理/打高尔夫球,但我认为它会满足您的要求。

如果我正确理解你的问题,使用正则表达式以外的其他工具可能会更清楚。以下确实将单词之间的任何白色 space 折叠为单个 space.

输入 qwwk.txt(加了一行)

..........

QWWK jhjh  kljdfh jklh jskdhf jkh PQXY
lhj ah jh sdlkjh PQXY jha slkdjh
PQXY jh alkjh ljk
kjhaksj dkjhsd KWWQ
hahs dkj h PQXY
.........

KWWQ in mid line doesn't trigger: QWWK a PQXY b KWWQ c QWWK d PQXY e KWWQ

命令perl qwwk.pl qwwk.txt

输出

..........

QWWK jhjh kljdfh jklh jskdhf jkh
lhj ah jh sdlkjh jha slkdjh
jh alkjh ljk
kjhaksj dkjhsd KWWQ
hahs dkj h PQXY
.........

KWWQ in mid line doesn't trigger: QWWK a PQXY b KWWQ c QWWK d PQXY e KWWQ

计划qwwk.pl

use strict; use warnings;
while(<>) {             # for each line
    my @out;
    my @words=split;    # get its words

    for my $i (0..$#words) {
        my $w=$words[$i];
        my $active = ($i==0 && $w eq q(QWWK)) .. ($i==$#words && $w eq q(KWWQ));
            # Keep track of where we are.  See notes below.
        push @out, $w unless $active and ($w eq q(PQXY));
            # Save words we want to keep
    } #foreach word

    print join(q( ), @out), qq(\n);     # Print the words we saved
} #foreach line

关键是 $active= FOO .. BAR 赋值中的触发器 (..) 运算符保持其状态,而不管周围发生什么。它将是真实的 行首的 QWWK (($i==0 && $w eq q(QWWK))) 到行尾的 KWWQ (($i==$#words && $w eq q(KWWQ))),无论中间有多少行。

单行

perl -Mstrict -Mwarnings -ne 'my @out; my @words=split; for my $i (0..$#words) { my $w=$words[$i]; my $active = ($i==0 && $w eq q(QWWK)) .. ($i==$#words && $w eq q(KWWQ)); push @out, $w unless $active and ($w eq q(PQXY)); } print join(q( ), @out), qq(\n);' qwwk.txt

此处的区别在于 -n 提供了 while(<>){} 循环,因此 -e 脚本中不包含它。 (另外,现在你知道我为什么在独立程序中使用 q()qq() 了;)。)

这是您尝试过的方法,还需要稍微多一点才能发挥作用

perl -0777 -wpe's{^(QWWK (?:(?!QWWK|KWWQ).)*? KWWQ)$}{  =~ s/PQXY//gr }egmsx' file

/e modifier 使其将替换端评估为代码,我们 运行 那里有一个正则表达式。

在该正则表达式中,/r 修饰符使其 return 更改后的字符串(而不是更改原始字符串,是什么让我们可以 运行 它在 </code> 上,这是只读)。</p> <p>上面的代码满足 <code>^QWWK-to-KWWQ$ 文本块不包含任何这些短语的要求,但一些评论可能会有所帮助。

我们不需要 非贪婪 .*? 因为 .*(在负前瞻之后)实际上停止在 KWWQ$。但这很难确定,.* 有可能吞噬 所有 直到最后的 KWWQ,包括所有其他可能的块和任何文本他们之间。

总而言之,我只是觉得 .*? 更安全、更简单,特别是 所需要的。

QWWK 必须开始一行(在问题中用 ^ 给出)才能成为块的标记。如果在块内发现额外的 QWWK 则整个块不匹配。但是,如果里面的 "extra" QWWK 恰好在一行的开头,那么

  • 块不匹配,因为里面有 QWWK

  • 实际上匹配的是 that QWWK

我在上面使用 /x 以便能够 space 出模式以提高可读性。