Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace only up to N matches on a line

Tags:

regex

perl

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

I.e., I'm looking for a middle ground between s/aa/bb/; and s/aa/bb/g;. I want to allow multiple substitutions, but only up to N times.

like image 963
paraba Avatar asked Apr 10 '12 19:04

paraba


1 Answers

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

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

That's not very efficient if there are far more than N matches. For that, we need to move the loop out of the regex engine. The next two methods are ways of doing that.

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 might be tempted to do something like

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

but that approach will fail 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'

And of course, starting from the beginning of the string every time could be expensive.

like image 119
ikegami Avatar answered Sep 22 '22 05:09

ikegami