Replace only up to N matches on the line

In Perl, how do I write a regular expression that replaces only up to N matches in a string?

Ie, I am looking for an intermediate point between s/aa/bb/; and s/aa/bb/g; . I want to allow multiple substitutions, but only up to N times.

+7
source share
4 answers

I can think of three reliable ways. The first is to replace everything after the Nth match with yourself.

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

It is not very effective if there are much more than N matches. To do this, we need to move the loop from the regex engine. The following two methods are ways to do this.

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

And this time, in place:

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

You may be tempted to do something like

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

but this approach will not work for other patterns and / or replacement expressions.

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

What you want is not possible in regular expressions. But you can put the replacement in a for loop:

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

result:

bbbbbbbbaaaaaaaaaaaaa

+1
source

You can use the /e flag, which evaluates the right side as an expression:

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

Here's a solution using the / e modifier, with which you can use perl code to create a replacement string:

  my $ count = 0;  $ string = ~ s {$ pattern} {$ Count ++;  if ($ count <$ limit) {$ Replace;  } else {$ & amp ;;  # faking no-op, replacing the original match.  }} X Β£ G;

With perl 5.10 or later, you can reset $ & (which is weird performance) and use $ {^ MATCH} with the / p modifier

  $ string = ~ s {$ pattern} {$ Count ++;  if ($ count <$ limit) {$ Replace;  } else {$ {^ MATCH};  }} Xegp;

Too bad you can't just do it, but you can't:

  last if $ count> = $ limit;
+1
source

All Articles