Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

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;

Comment on Replace the nth occurence
Download Code
Re: Replace the nth occurence
by Ratazong (Prior) 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 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 AnomalousMonk (Monsignor) 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 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;

    Output:

    a,b|c,d
Re: Replace the nth occurence
by choroba (Abbot) on Nov 21, 2012 at 10:39 UTC
    TIMTOWTDI:
    #!/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 trizen (Friar) 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 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 LanX (Canon) 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'

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1004836]
Approved by davido
Front-paged by ysth
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (7)
As of 2014-07-11 05:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (218 votes), past polls