我们真的可以不使用惰性量词吗?

4
许多人说在正则表达式中我们可以不使用懒惰量词,但我最近遇到了一个问题,没有它们我无法解决(这里我使用的是sed)。
我想处理的字符串由以单词rate分隔的子字符串组成,例如:
anfhwe9.<<76xnf9247 rate 7dh3_29snpq+074j rate 48jdhsn3gus8 rate

我希望将那些子字符串(除了单词“rate”)替换为每个3个破折号(---); 结果应该是:

---rate---rate---rate

据我了解(我不懂Perl),使用惰性量词可以很容易地实现。在vim中也有惰性量词;我使用以下命令完成了此操作:

:s/.\{-}rate/---rate/g

\{-} 告诉 Vim 匹配尽可能少的字符。

不过,Vim 只是一个文本编辑器,我需要在许多机器上运行这个脚本,其中一些机器没有安装 Perl。如果您可以告诉正则表达式不匹配类似于 .*[^(rate)]rate 的原子分组,则也可以解决此问题,但这种方法并不奏效。

有什么办法可以使用 POSIX 正则表达式来实现这个功能吗,或者说这是不可能的?

6个回答

3

在这种情况下,我会使用split()方法:

perl -n -e 'print join ("rate", ("---") x split /rate/)' [input-file]

这绝对是一个正则表达式可能不是最佳解决方案的情况,但他特别关注的是可能没有安装 Perl 的环境。 - Asmor
1
我不太确定 - 帖子被标记为Perl。无论如何,在几乎所有语言中都实现了split(或tokenize),这只需要简单的字符串匹配来进行拆分。例如,可以在C中使用strtok来完成。 - Mike Sokolov
split支持正则表达式吗?如果我想用一个像r[aei]te这样的正则表达式来分隔,而不是一个固定的分隔符,该怎么办?在这种情况下,split无法工作。 - user854270
在Perl和Java中,split支持正则表达式。我相信Python也有类似的东西?不过我不确定你正在使用什么环境? - Mike Sokolov

2

如果不使用惰性量词或负向前瞻(POSIX都不支持),这就不容易了,但是这个方法似乎可行。

([^r]*((r($|[^a]|a([^t]|$)|at([^e]|$))))?)+rate

我模糊地记得POSIX字符类有点挑剔。如果它们尚未符合POSIX标准,则您可能需要修改该正则表达式中的字符类。请注意保留HTML标签。

@ikegami 我错过了它总是以rate结尾的事实,所以我使rate变成了非可选项,并且我还将最后一个*更改为+,以要求在“rates”之间至少有一个字符。 - Asmor

2

输入中是否有任何保证不出现的字符?例如,如果 '!' 不会出现,您可以将输入转换为替换该唯一字符,然后对转换后的输入进行全局替换:

sed 's/ rate /!/g' < input | sed -e 's/[^!]*/---/g' -e 's/!/rate/g'

另一种选择是使用awk的split命令类似于上面提到的perl建议,假设awk比perl更加可靠。
awk '
{   ans="---"
    n=split($0, x, / rate /);
    while ( n-- ) { ans = ans "rate---";}
    print ans
}'

1

你不关心子字符串的内容这一事实为你提供了很多选项。例如,可以参考Bob Lied的建议——即使输入中可能包含“!”,你也可以先将其更改为其他字符:

sed -e 's/!/./g' -e 's/rate/!/g' -e 's/[^!]\+/---/g' -e 's/!/rate/g' <input >output

0

使用 awk 命令:

awk -Frate '{ 
  for (i = 0; ++i <= NF;) 
    $i = (i == 1 || i == NF) && $i == x ? x : "---" 
  }1' OFS=rate infile   

0
或者,awk 'BEGIN {OFS=FS="rate"} {for (i=1; i<=NF-1; i++) {$i = "---"}; print}'


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接