8

在 Perl 中,如何编写一个正则表达式,每个字符串最多只替换 N 个匹配项?

即,我正在寻找 和 之间的中间s/aa/bb/;立场s/aa/bb/g;。我想允许多次替换,但最多只能替换 N 次。

4

4 回答 4

5

我能想到三种可靠的方法。第一种是将第 N 次匹配后的所有内容替换为自身。

my $max = 5;
$s =~ s/(aa)/ $max-- > 0 ? 'bb' : $1 /eg;

如果匹配的次数远远超过 N,那效率就不是很高。为此,我们需要将循环移出正则表达式引擎。接下来的两种方法是这样做的方法。

my $max = 5;
my $out = '';
$out .= $1 . 'bb' while $max-- && $in =~ /\G(.*?)aa/gcs;
$out .= $1 if $in =~ /\G(.*)/gcs;

而这一次,就地:

my $max = 5;
my $replace = 'bb';
while ($max-- && $s =~ s/\G.*?\Kaa/$replace/s) {
   pos($s) = $-[0] + length($replace);
}

你可能会想做类似的事情

my $max = 5;
$s =~ s/aa/bb/ for 1..$max;

但该方法对于其他模式和/或替换表达式将失败。

my $max = 5;
$s =~ s/aa/ba/ for 1..$max;  # XXX Turns 'aaaaaaaa'
                             #     into 'bbbbbaaa'
                             #     instead of 'babababa'

当然,每次从字符串的开头开始可能会很昂贵。

于 2012-04-10T22:35:42.923 回答
2

您可以使用/e评估右侧的标志作为表达式:

my $n = 3;    
$string =~ s/(aa)/$n-- > 0 ? "bb" : $1/ge;
于 2012-04-10T21:20:34.223 回答
2

你想要的在正则表达式中是不可能的。但是您可以将替换放在 for 循环中:

my $i;
my $aa = 'aaaaaaaaaaaaaaaaaaaa';
for ($i=0;$i<4;$i++) {
    $aa =~ s/aa/bb/;
}
print "$aa\n";

结果:

bbbbbbbbaaaaaaaaaaaa

于 2012-04-10T20:00:55.260 回答
1

这是使用 /e 修饰符的解决方案,您可以使用 perl 代码生成替换字符串:

  我的 $count = 0;
  $string =~ s{ $pattern }
              {
                $计数++;
                如果($count < $limit){
                  $替换;
                } 别的 {
                  $&; # 伪造无操作,替换为原始匹配。
                }
              }xeg;

使用 perl 5.10 或更高版本,您可以删除 $& (具有奇怪的性能并发症)并通过 /p 修饰符使用 ${^MATCH}

  $string =~ s{ $pattern }
              {
                $计数++;
                如果($count < $limit){
                  $替换;
                } 别的 {
                  ${^匹配};
                }
              }xegp;

太糟糕了,你不能这样做,但你不能:

  最后如果 $count >= $limit;

于 2012-04-11T20:19:38.367 回答