Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

Replace the nth occurence

by Anonymous Monk
on Nov 21, 2012 at 05:17 UTC ( #1004836=perlquestion: print w/replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I need to replace the nth occurence of a character in a string.Currenly m tryng to replace the 2nd comma in ths string.I have used the following code for that.Can anyone give a better suggestion?

my $count = 3; my $str = "a,b,c,d"; $str =~ s/(,)/--$count == 1 ? "|":$1/ge; print $str;

Replies are listed 'Best First'.
Re: Replace the nth occurence
by AnomalousMonk (Canon) on Nov 21, 2012 at 06:02 UTC

    Another approach (\K available with 5.10+):

    >perl -wMstrict -le "my $nth = 4; my $str = 'a,bb,ccc,dddd,eeeee,ffffff'; ;; --$nth; $str =~ s{ (?: , [^,]*){$nth} \K , }{|}xms; print qq{'$str'}; " 'a,bb,ccc,dddd|eeeee,ffffff'

      ++. This is the most efficient approach.

      When's the last time you used duct tape on a duct? --Larry Wall
Re: Replace the nth occurence
by Ratazong (Monsignor) on Nov 21, 2012 at 05:32 UTC
    Your code is similar to the one in the FAQ (except that you count downwards). And it works. What do you think is wrong with it?
      Well, there is actually a much better way, as illustrated by Anomalous Monk.

      When's the last time you used duct tape on a duct? --Larry Wall
Re: Replace the nth occurence
by Kenosis (Priest) on Nov 21, 2012 at 07:22 UTC

    I like your solution for replacing the nth occurrence of a character within a string. I don't know whether the following is a 'better suggestion,' but what about initializing $n (for the nth) to the desired occurrence:

    use strict; use warnings; # Replace second comma my $n = 2; my $str = 'a,b,c,d'; $str =~ s/(,)/!--$n ? '|' : $1/ge; print $str;


Re: Replace the nth occurence
by choroba (Chancellor) on Nov 21, 2012 at 10:39 UTC
    #!/usr/bin/perl use warnings; use strict; use feature 'say'; sub replace { my ($string, $from, $to, $count) = @_; my $pos = '0E0'; # plain 0 means the string begins with $from while ($count-- and $pos >= 0) { $pos = index $string, $from, $pos eq '0E0' ? $pos : $pos + 1; } substr $string, $pos, 1, $to if $pos > 0; return $string; } say replace($_, ',' => '|', 2) for qw( a,b,c,d pq,rs,tu,vw ,s,t,a,r,t ,,yuck 1,2 );
    لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Replace the nth occurence
by Anonymous Monk on Nov 21, 2012 at 12:23 UTC
    You would do well to build a generalized and easy-to-understand way to do this, instead of chicken-scratch solutions that are both non-obvious and basically unmaintainable. Between index and substr you could build a function that loops to find the correct index and then does the replacement; given the input string, the string to be replaced, and the occurrence number. You are almost guaranteed to be happier with this "inefficient" way of doing it, because it is a generalized way of doing something that, no doubt, will wind up being used in a bunch of different situations. CPAN also has thousands of string utilities.
Re: Replace the nth occurence
by trizen (Hermit) on Nov 21, 2012 at 10:56 UTC
    A general solution:
    my $nth = 4; my $str = 'a=>bb=>ccc=>dddd=>eeeee=>ffffff'; while ($str =~ /=>/g) { if (--$nth == 0) { substr($str, $-[0], $+[0] - $-[0], '~~|~~'); last; } } print "$str\n";

      In the sprit of this, also a generalized approach. No benchmarking done for two regexes used or versus other approaches. Note also that the index of the occurrence of the pattern which will be replaced is now zero-based. (Also: This approach could be generalized yet further by passing either a plain replacement string or a code reference. The string/reference could then be fed as appropriate to one of two  s/// substitutions, one without a /e regex modifier, one with. Code of a replacement reference would have access to all capture variables, etc.) All tests pass.

Re: Replace the nth occurence
by grizzley (Chaplain) on Nov 21, 2012 at 12:21 UTC
    my $count = 1; my $str = "a,b,c,d"; $str =~ s/((,.*?){$count}),/$1|/; print $str;
Re: Replace the nth occurence
by rcrews (Novice) on Nov 21, 2012 at 05:37 UTC
    The built-in substr function does it:
    my $count = 3; my $str = "a,b,c,d"; substr $str, $count, 1, '|'; print $str;
      Have you tried to replace the 2nd comma in the following string using your code? Seems there is room for improvement...
      my $str = "aa,bb,cc,dd";
        It doesn't even work on the example string for any count other than three--it was just a coincidence that it happened to be in the right position in that case.

        When's the last time you used duct tape on a duct? --Larry Wall
Re: Replace the nth occurence
by LanX (Chancellor) on Nov 21, 2012 at 23:16 UTC
    ++AnomalousMonk for demonstrating what \K is good for! =)

    Maybe I'm boring ... but I prefer more explicit code with split-and-join over regex-acrobatics:

    my $nth = 4; my $str = 'a,bb,ccc,dddd,eeeee,ffffff'; my @str= split /,/,$str; $str = join ( "," , @str[0..$nth-1] ) . "|" . join ( "," , @str[$nth.. +$#str] ); print qq{'$str'};

    Cheers Rolf

      ... I prefer more explicit code ... over regex-acrobatics ...

      And, in general, so do I. However, I spent so much effort figuring out regexes and they offer so many bright doodads and shiny gewgaws that my first reaction to a question like the OP is "Hey, I should be able to do this (or do it better) with a regex like..." So I put together a regex and it works – sort of; and I see how I can fix it by putting in a positive look-ahead here; and that makes it better, but still not quite, but if I put in an alternation there...; oops, now it doesn't work at all; oh, I see, that should have been...; that's a lot better, but there's still this corner case... And so it goes. But in the end I learn a little more about regexes, which makes me a little more likely to turn to them in the future... This stuff isn't syntactic sugar, it's syntactic heroin!

        > This stuff isn't syntactic sugar, it's syntactic heroin!

        LOL ... true so true.

        I really miss the time when I needed to optimize stuff in 68000-assembler.

        So for me hacking regexes is a methadone program! ;-)

        Cheers Rolf

Re: Replace the nth occurence (testing and benchmarks)
by tobyink (Abbot) on Nov 23, 2012 at 15:11 UTC

    Testing some of the existing answers...

    The fastest seems to be Re: Replace the nth occurence by trizen.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: Replace the nth occurence
by Anonymous Monk on Jan 27, 2017 at 15:32 UTC
    I came across this thread whilst looking to do something more complex. Essentially the most general regex case where the replacement might have $1 etc in it. Thought I'd post my solution, along with the comments I put in my code (note that the e(vals) aren't a concern for my usage, but others might want to guard against injection):
    # We want to replace the $nth match of $regex in $value # with $replacement # OK, so doing the nth regex replacement in a string just # isn't going to be that readable, but at least it's # concise. I'll try to explain: # As we go through the regex matches we want to count # down from $nth to zero being when we want to replace. # $& is just what we matched, so we'll write that when # we're not replacing ie when $nth!=0. # qq{} is just quotation that saves us escaping ' or " in # the quotation string. # So here we're creating a string where $replacement is # parsed into quotations eg --$nth ? $& : qq{new text $1} $rep_eval = "--\$nth ? \$& : qq{$replacement}"; # Then here we have our usual regex search and replace # which we perform g(lobally) and e(val) twice. # 1st e(val) parses $rep_eval, and the 2nd evaluates the # "<if> ? <true> : <false>" expression for each match. $value =~ s/$regex/$rep_eval/gee;

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1004836]
Approved by davido
Front-paged by ysth
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (10)
As of 2017-02-22 18:07 GMT
Find Nodes?
    Voting Booth?
    Before electricity was invented, what was the Electric Eel called?

    Results (334 votes). Check out past polls.