MuraliKrishna
MuraliKrishna

Reputation: 7143

Perl replace nth substring in a string

I have a scenario in which I need to replace the nth sub-string in a string.

s/sub-string/new-string/g; will replace all the sub strings, but I need to do for a particular occurrence.

Please help me with this.

Upvotes: 3

Views: 8232

Answers (7)

druud62
druud62

Reputation: 1

See perlvar for @- and @+.

  my $str= "one two three four five";
  if ( $str =~ /(?: (\w+) .*? ){3}/x ) {
    substr $str, $-[1], $+[1] - $-[1], "-c-e-n-s-o-r-e-d-";
  }
  print $str, "\n";

The regex finds the 3rd instance, and captures its start-word in $1.

Upvotes: 0

cppcoder
cppcoder

Reputation: 23095

For replacing the nth occurrence of a string using sed, you can use this command:

sed 's/find_string/replace_string/n'

For replacing the substring, we need to know what you want to replace. Give an example.

Upvotes: 3

Prince John Wesley
Prince John Wesley

Reputation: 63688

Try this:

 s/((old-string).*?){2}\2/\1\1new-string/

Upvotes: 1

DavidO
DavidO

Reputation: 13942

I'm really a believer that there's no point building extra complexity into a regular expression unless it's truly necessary to do so (or unless you're just having fun). In code I actually planned to use I would keep it simple, like this:

my $string = "one two three four five";

$string =~ m/\w+\s*/g for 1 .. 2;
substr( $string,pos($string) ) =~ s/(\w+)/3/;
print "$string\n";

Using the m//g in scalar context causes it to match one time per iteration of the for loop. On each iteration pos() keeps track of the end of the most recent submatch on $string. Once you've gone through 'n' iterations (two in this case), you can plug pos() into substr(). Use substr($string... as an lvalue. It will constrain the regexp match to begin at whatever position you tel it in the second arg. We're plugging pos in there, which constrains it to take its next match wherever the last match left off.

This approach eliminates an explicit counter (though the for loop is essentially the same thing without naming a counter variable). This approach also scales better than a s//condition ? result : result/eg approach because it will stop after that third match is accomplished, rather than continuing to try to match until the end of a potentially large string is reached. In other words, the s///eg approach doesn't constrain the matching, it only deals conditionally with the outcome of an arbitrarily large number of successful matches.

In a previous question on the same topic I once embedded a counter in the left side of the s/// operator. While it worked for that specific case, it's not an ideal solution because it's prone to being thrown off by backtracking. That's another case where keeping it simple would have been the best approach. I mention it here so that you can avoid temptation to try such a trick (unless you want to have fun with backtracking).

The approach I've posted here, I believe is very clear; you look at it and know what's happening: match twice, keep track of last match position, now match a third time with substitution. You can have clever, you can have efficient, and you can have clear. But sometimes you can't have all three. Here you get efficient and clear.

Upvotes: 1

Dallaylaen
Dallaylaen

Reputation: 5308

You can also do like this

my $i=0;
s/(old-string)/++$i%3 ? $1 : "new_string"/ge;

Upvotes: 1

TLP
TLP

Reputation: 67900

This question might be interesting: Perl regex replace count

You might do something like this:

use strict;
use warnings;

my $count = 3;
my $str = "blublublublublu";
$str =~ s/(lu)/--$count == 0 ? "LA":$1/ge;
print $str;

Upvotes: 1

LeleDumbo
LeleDumbo

Reputation: 9340

Try this:

s/(sub-string{2,2})sub-string/$1new-string/

adjust 2 according to your needs (it's your 'n'- 1). Note that there may no separators exist between those substrings. e.g. 'abcabcabc' would work but 'abcdefabcabc' won't

Upvotes: 1

Related Questions