How can I replace the nth match in a Perl regex?

Following the earlier question of extracting n'th regex matching , now I need to substitute the match if it is found.

I thought I could define an extraction routine and call it in the substitution using the /e modifier. I was clearly wrong (admittedly, I had an XY problem ).

 use strict; use warnings; sub extract_quoted { # Γ  la codaddict my ($string, $index) = @_; while($string =~ /'(.*?)'/g) { $index--; return $1 if(! $index); } return; } my $string = "'How can I','use' 'PERL','to process this' 'line'"; extract_quoted ( $string, 3 ); $string =~ s/&extract_quoted($string,2)/'Perl'/e; print $string; # Prints 'How can I','use' 'PERL','to process this' 'line' 

There are, of course, many other problems with this technique:

  • What to do if the same matches at different positions?
  • What to do if no match is found?

In light of this situation, I wonder how this can be implemented.

+4
source share
5 answers

Or you can do something like this

 use strict; use warnings; my $string = "'How can I','use' .... 'perl','to process this' 'line'"; my $cont =0; sub replacen { # auxiliar function: replaces string if incremented counter equals $index my ($index,$original,$replacement) = @_; $cont++; return $cont == $index ? $replacement: $original; } #replace the $index n'th match (1-based counting) from $string by $rep sub replace_quoted { my ($string, $index,$replacement) = @_; $cont = 0; # initialize match counter $string =~ s/'(.*?)'/replacen($index,$1,$replacement)/eg; return $string; } my $result = replace_quoted ( $string, 3 ,"PERL"); print "RESULT: $result\n"; 

A bit ugly "global" $ cont variable that can be polished, but you get the idea.

Update: more compact version:

 use strict; my $string = "'How can I','use' .... 'perl','to process this' 'line'"; #replace the $index n'th match (1-based counting) from $string by $replacement sub replace_quoted { my ($string, $index,$replacement) = @_; my $cont = 0; # initialize match counter $string =~ s/'(.*?)'/$cont++ == $index ? $replacement : $1/eg; return $string; } my $result = replace_quoted ( $string, 3 ,"PERL"); print "RESULT: $result\n"; 
+3
source

EDIT: leonbloy came up with this solution first. If your temptation is to boost it, first raise leonbloy.

Somewhat inspired by leonbloy (formerly) answer:

 $line = "'How can I','use' 'PERL' 'to process this';'line'"; $n = 3; $replacement = "Perl"; print "Old line: $line\n"; $z = 0; $line =~ s/'(.*?)'/++$z==$n ? "'$replacement'" : "'$1'"/ge; print "New line: $line\n"; 

  Old line: 'How can I', 'use' 'PERL' 'to process this'; 'line'
 New line: 'How can I', 'use' 'Perl' 'to process this'; 'line'
+4
source

If the regular expression is not much more complicated than yours, you can follow the split with editing and join :

 $line = "'How can I','use' 'PERL','to process this' 'line'"; $n = 3; $new_text = "'Perl'"; @f = split /('.*?')/, $line; # odd fields of @f contain regex matches # even fields contain the text between matches $f[2*$n-1] = $new_text; $new_line = join '', @f; 
0
source

See perldoc perlvar :

 use strict; use warnings; use Test::More tests => 5; my %src = ( q{'I want to' 'extract the word' 'PERL','from this string'} => q{'I want to' 'extract the word' 'Perl','from this string'}, q{'What about', 'getting','PERL','from','here','?'} => q{'What about', 'getting','Perl','from','here','?'}, q{'How can I','use' 'PERL','to process this' 'line'} => q{'How can I','use' 'Perl','to process this' 'line'}, q{Invalid} => q{Invalid}, q{'Another invalid string'} => q{'Another invalid string'} ); while ( my ($src, $target) = each %src ) { ok($target eq subst_n($src, 3, 'Perl'), $src) } sub subst_n { my ($src, $index, $replacement) = @_; return $src unless $index > 0; while ( $src =~ /'.*?'/g ) { -- $index or return join(q{'}, substr($src, 0, $-[0]), $replacement, substr($src, $+[0]) ); } return $src; } 

Conclusion:

  C: \ Temp> pw
 1..5
 ok 1 - 'Another invalid string'
 ok 2 - 'How can I', 'use' 'PERL', 'to process this' 'line'
 ok 3 - Invalid
 ok 4 - 'What about', 'getting', 'PERL', 'from', 'here', '?'
 ok 5 - 'I want to' 'extract the word' 'PERL', 'from this string' 

Of course, you need to decide what happens if an invalid $index passed in or if the required match is not found. I just returned the original line to the code above.

0
source

After working through the answer to the previous question , compare n-1 times, and then replace the next. Remembering patterns saves poor Perl from having to recompile the same patterns over and over again.

 my $_quoted = qr/'[^']+'/; # ' fix Qaru highlighting my %_cache; sub replace_nth_quoted { my($string,$index,$replace) = @_; my $pat = $_cache{$index} ||= qr/ ^ ( # $1 (?:.*?$_quoted.*?) # match quoted substrings... {@{[$index-1]}} # $index-1 times ) $_quoted # the ${index}th match /x; $string =~ s/$pat/$1$replace/; $string; } 

for instance

 my $string = "'How can I','use' 'PERL','to process this' 'line'"; print replace_nth_quoted($string, 3, "'Perl'"), "\n"; 

exits

  'How can I', 'use' 'Perl', 'to process this' 'line' 
0
source

Source: https://habr.com/ru/post/1305689/


All Articles