http://www.perlmonks.org?node_id=475052

krisahoch has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I have an interesting problem where I need to replace a specific instance of a word in a string.

Example String:Fsih my test variable is fsihd or is it gfsih or gfsihd fsih

I may want to replace the third instance of 'fsih' with 'fish', or the fourth, or even the last. How would you do that? I have tried several ways (regexp) but I failed each time.

Thank you in advance

Kristofer

Replies are listed 'Best First'.
Re: Replacing a specfied instance of a pattern in a string
by ikegami (Patriarch) on Jul 14, 2005 at 21:32 UTC
    my $str = 'Fsih my test variable is fsihd or is it gfsih or gfsih +d fsih'; # 1111 2222 3333 + 4444 my $substr = 'fsih'; my $replace = 'fish'; my $instance = 3; # 3rd print("$str\n"); my $pos = -length($substr); for (;;) { last if !$instance--; $pos = index($str, $substr, $pos + length($substr)); last if $pos < 0; } substr($str, $pos, length($substr), $replace) if $pos >= 0; print("$str\n");

    Update: Fixed off-by-one error.

      A tad simpler looping, and case-insensitive:
      my $foo = 'Fsih my test variable is fsihd or is it gfsih or gfsihd f +sih'; my $from = 'fsih'; my $which = 3; my $to = 'FISH'; # makes it easy to see $foo =~ /\Q$from\E/ig for (1..$which); substr($foo, pos($foo) - length($from), length($from), $to) if pos($fo +o); print "Foo = $foo\n";

      Caution: Contents may have been coded under pressure.
        That reminds me of /c!
        my $str = 'Fsih my test variable is fsihd or is it gfsih or gfsih +d fsih'; # 1111 2222 3333 + 4444 my $substr = 'fsih'; my $regexp = qr/\Q$substr\E/; my $replace = 'fish'; my $instance = 3; # 3rd print("$str\n"); scalar $str =~ /\G.*?$regexp/gc while --$instance; $str =~ s/\G(.*?)$regexp/$1$replace/; print("$str\n");

        The /c is needed in case there are less than $instance instances of the search string.

      Now, is there a way to do it with Regular Expression?
        my $str = 'Fsih my test variable is fsihd or is it gfsih or gfsih +d fsih'; # 1111 2222 3333 + 4444 my $substr = 'fsih'; my $regexp = qr/\Q$substr\E/; my $replace = 'fish'; my $instance = 3; # 3rd print("$str\n"); my $pre_count = $instance - 1; $str =~ s/((?:$regexp.*?){$pre_count})$regexp/$1$replace/; print("$str\n");

        You might have noticed both of these are case sensitive. Both can be made case-insensitive. The non-regexp version is most likely much faster. The regexp version can handle regexps instead of constant strings.

Re: Replacing a specfied instance of a pattern in a string
by Roy Johnson (Monsignor) on Jul 14, 2005 at 21:40 UTC
    One way:
    my $foo = 'Fsih my test variable is fsihd or is it gfsih or gfsihd fsi +h'; # Replace the 3rd $foo =~ s/((?:fsih.*?){2})fsih/$1fish/i or warn "Not found"; print "Foo = $foo\n";

    Caution: Contents may have been coded under pressure.

      or to pass the index in use:

      use warnings; use strict; my $foo = 'Fsih my test variable is fsihd or is it gfsih or gfsihd fsi +h'; my $i = 2; # Replace the 3rd $foo =~ s/((?:fsih.*?){$i})fsih/$1fish/i or warn "Not found"; print "Foo = $foo\n";

      Perl is Huffman encoded by design.
Re: Replacing a specfied instance of a pattern in a string (2 more)
by tye (Sage) on Jul 15, 2005 at 05:17 UTC
    my $i= 0; $str =~ s/(...)/ 3 == ++$i ? 'fish' : $1 /ge;
    or
    my $i = 3; 0 while $str =~ /fsih/gi && --$i; substr( $str, $-[0], $+[0]-$-[0], 'fish' ) if ! $i;

    - tye        

Re: Replacing a specfied instance of a pattern in a string
by Jasper (Chaplain) on Jul 15, 2005 at 11:01 UTC
    my $instance = 3; $string = s/fsih/--$instance?$&:'fish'/eg;