Re: Replace the nth occurence
by AnomalousMonk (Archbishop) 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] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
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? | [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] |
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
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Replace the nth occurence
by choroba (Cardinal) 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] [Watch: Dir/Any] [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] [Watch: Dir/Any] |
Re: Replace the nth occurence
by trizen (Hermit) 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] [Watch: Dir/Any] [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] [Watch: Dir/Any] [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] [Watch: Dir/Any] [d/l] |
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] [Watch: Dir/Any] [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] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
Re: Replace the nth occurence
by LanX (Saint) 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] [Watch: Dir/Any] [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] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] |
Re: Replace the nth occurence (testing and benchmarks)
by tobyink (Canon) 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] [Watch: Dir/Any] [d/l] |
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;
| [reply] [Watch: Dir/Any] [d/l] |