一行最多只能替换N个匹配项

时间:2012-04-10 19:47:57

标签: regex perl

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

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

4 个答案:

答案 0 :(得分: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'

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

答案 1 :(得分:2)

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

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

结果:

  

bbbbbbbbaaaaaaaaaaaa

答案 2 :(得分:1)

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

my $n = 3;    
$string =~ s/(aa)/$n-- > 0 ? "bb" : $1/ge;

答案 3 :(得分:1)

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

  my $count = 0;
  $string =~ s{ $pattern }
              { 
                $count++;
                if ($count < $limit ) { 
                  $replace;
                } else { 
                  $&; # faking a no-op, replacing with the original match.
                }
              }xeg;

使用perl 5.10或更高版本,您可以放弃$&amp; (这很奇怪 性能并发症)并通过/ p修饰符使用$ {^ MATCH}

  $string =~ s{ $pattern }
              {
                $count++;
                if ($count < $limit ) { 
                  $replace;
                } else { 
                  ${^MATCH};
                }
              }xegp;

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

  last if $count >= $limit;