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;
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? | [reply] |
|
| [reply] |
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;
| [reply] [d/l] |
|
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";
| [reply] [d/l] |
|
| [reply] |
Re: Replace the nth occurence by AnomalousMonk (Prior) on Nov 21, 2012 at 06:02 UTC |
>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'
| [reply] [d/l] |
|
| [reply] |
Re: Replace the nth occurence by Kenosis (Deacon) 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
| [reply] [d/l] [select] |
Re: Replace the nth occurence by choroba (Prior) 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
);
| [reply] [d/l] |
Re: Replace the nth occurence by trizen (Friar) on Nov 21, 2012 at 10:56 UTC |
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";
| [reply] [d/l] |
|
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.
| [reply] [d/l] [select] |
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;
| [reply] [d/l] |
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. | [reply] |
Re: Replace the nth occurence by LanX (Monsignor) on Nov 21, 2012 at 23:16 UTC |
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'};
| [reply] [d/l] |
|
... 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!
| [reply] |
|
| [reply] |
Re: Replace the nth occurence (testing and benchmarks) by tobyink (Monsignor) 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'
| [reply] [d/l] |
|
|