【问题标题】:perl: how to remove particular word or pattern in between two patternsperl:如何删除两个模式之间的特定单词或模式
【发布时间】:2019-09-14 16:28:48
【问题描述】:

我想使用 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 的模式。

【问题讨论】:

  • 您是否关心保留单词之间的特定空格?如果没有,split 结果上的内部循环将允许您逐字使用范围运算符。

标签: regex perl multiline text-processing


【解决方案1】:

您可以使用范围运算符:

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

【讨论】:

  • 我也想满足 (?!QWWK|KWWQ) 的条件。)(即中间不会有任何 QWWK 和 KWWQ
  • 您能否将这个特定案例添加到输入样本中?
【解决方案2】:

更新: 要仅在 ^QWWK 和 KWWQ$ 之间不存在 QWWK 或 KWWQ 时替换 PQXY,请尝试以下操作:

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

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

【讨论】:

  • 我也想满足(?!QWWK|KWWQ).)的条件(即中间不会有QWWK和KWWQ
  • 我用问题中的多行示例数据对其进行了测试。似乎适用于多行。
  • 如果要排除内部QWWK | KWWQ 跨多行,您需要先读取整个文件。 -p 命令行选项此时只会读取一行,因此 /m 正则表达式选项并没有真正使用,因此没有多行匹配。
  • 假设我想删除新行而不是 PQXY 怎么做。
  • s/\R//g 将删除包含多个换行符的字符串中的换行符
【解决方案3】:

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

输入 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 &amp;&amp; $w eq q(QWWK))) 到行尾的KWWQ (($i==$#words &amp;&amp; $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(&lt;&gt;){} 循环,因此-e 脚本中不包含该循环。 (另外,现在你知道我为什么在独立程序中使用q()qq() 了;)。)

【讨论】:

  • 我会试试这个。我得稍微了解一下
【解决方案4】:

这是您尝试过的方法,还需要一点点才能工作

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

/emodifier 使其将替换端评估为代码,我们在那里运行正则表达式。

在该正则表达式中,/r 修饰符使其返回更改后的字符串(而不是更改原始字符串,这允许我们在只读的$1 上运行它)。

上面的代码满足^QWWK-to-KWWQ$ 文本块不包含这些短语的要求,但一些 cmets 可能会有所帮助。

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

总的来说,我发现.*? 更安全、更简单,特别是因为这所需要的。

QWWK 必须以一行开头(在问题中以^ 给出)作为块的标记。如果在块内发现额外的QWWK,则整个块不匹配。但是,如果里面的“额外”QWWK 恰好位于行首,那么

  • 本来是块的内容不匹配,因为里面有QWWK

  • 实际上以 that QWWK987654339@

  • 开头的块匹配

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 2014-07-30
    • 1970-01-01
    • 2021-07-02
    • 2022-08-15
    • 2018-02-13
    相关资源
    最近更新 更多