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

Hello

I have a problem, I have a string where I like to replace a part of it with something else. And I know I should be able to so using regex. I just don't now how. The only think I like to do is to replace $1 with an other number.

Here is the string I like to manipulate:
http://adserver.adtech.de/?addyn|2.0|323|91793|1|277|target=_blank

And here is my regex:
.*[\|/].*[\|/].*[\|/].*[\|/].*[\|/](.*)[\|/].*

So for instance I like to replace 277 with 976, so the new string will be:
http://adserver.adtech.de/?addyn|2.0|323|91793|1|976|target=_blank

I will be very pleased if somebody is able to help.

Regards
krisravn

Replies are listed 'Best First'.
Re: Replace part of a regex match
by kyle (Abbot) on Dec 23, 2008 at 21:03 UTC

    If you're really committed to the regexp, this works:

    my $u = 'http://adserver.adtech.de/?addyn|2.0|323|91793|1|277|target=_ +blank'; print "$u\n"; $u =~ s/(.*\|.*\|.*\|.*\|.*\|).*(\|.*)/${1}976$2/; print "$u\n"; __END__ http://adserver.adtech.de/?addyn|2.0|323|91793|1|277|target=_blank http://adserver.adtech.de/?addyn|2.0|323|91793|1|976|target=_blank

    You might also try using split and join:

    my $u = 'http://adserver.adtech.de/?addyn|2.0|323|91793|1|277|target=_ +blank'; print "$u\n"; my @fields = split /\|/, $u; $fields[5] = 976; $u = join '|', @fields; print "$u\n"; __END__ http://adserver.adtech.de/?addyn|2.0|323|91793|1|277|target=_blank http://adserver.adtech.de/?addyn|2.0|323|91793|1|976|target=_blank

    I also think it would be good to have a more specific expression. For example, instead of ".*", use "\d+" to make sure it's not an empty field and to make sure that it really does have digits and not some other junk you weren't expecting.

      kyle, could the following change to the your regex be a little closer to what the OP sought...
      $u =~ s/(.*\|{5}).*(\|.*)/${1}976$2/;

      A user level that continues to overstate my experience :-))
        The regex expression .*\|{5} matches anything followed by exactly five '|' characters in a row, which is not what is present in the example of the OP.

        A regex something like the following might be better (although it is still a bit awkward):

        >perl -wMstrict -le "my $u = 'http://foo.bar.de/?baz|2.0|323|91793|1|277|fee=_fie'; print $u; $u =~ s{ \A ((?: [^|]* \|){5}) \d+ (\|.*) \z }{${1}976$2}xms; print $u; " http://foo.bar.de/?baz|2.0|323|91793|1|277|fee=_fie http://foo.bar.de/?baz|2.0|323|91793|1|976|fee=_fie
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Replace part of a regex match
by monarch (Priest) on Dec 24, 2008 at 05:58 UTC
    I know that, in the past, I've just wanted to jam something into $1 - it seems a natural way of expressing what I want to do. Unfortunately Perl doesn't do it this way. So you've got to think of other means. And there are many approaches (see perlre).

    One method might be to calculate where the match of your capture finishes, calculate where the capture started, and replace that portion of the string directly using substr.

    Example code:

    use strict; my $val = 'http://adserver.adtech.de/' . '?addyn|2.0|323|91793|1|277|target=_blank'; my $re = qr/ .*\|.*\|.*\|.*\|.*\| (.*) (?=\|.*) # look-ahead /x; if ( $val =~ m/$re/g ) { print( "found: \"$1\"\n" ); print( "pos is: " . scalar( pos( $val ) ) . "\n" ); print( "behind pos is: \"" . substr( $val, pos( $val ) - length( $1 ), length( $1 ) ) . "\"\n" ); # perform substitution here using calculated offsets substr( $val, pos( $val ) - length( $1 ), length( $1 ) ) = "moo"; print( "$val\n" ); }

    The problem is that you have to use a look-ahead to prevent the pos function returning the end of the entire regexp match.. The other problem is that you have to use the /g (global) flag on your match to ensure the position is calculated.

    This code replaces the "277" with the word "moo". Try it!

      I might also offer the suggestion that the regexp you've chosen is hard work for Perl. It means capture as much as possible, and only then check if a pipe symbol comes afterwards. That there are several of these ensures that this regexp isn't that efficient.

      You might like to try something like the following:

      my $re = qr/ (?: # start non-capturing group [^\|]+ # as many non-pipe characters as possible \| # followed by a pipe character ){5} # and ensure there are 5 such groups in a row ([^\|]+) # capture as many non-pipe chars as possible /x;

      Using Benchmark the new regular expression here was 400% faster on my computer:

      Rate oldway newway oldway 108225/s -- -81% newway 563910/s 421% --

      Update: corrected URL

Re: Replace part of a regex match
by JavaFan (Canon) on Dec 24, 2008 at 12:01 UTC
    It seems to me that you want to replace the penultimate group that's separated with vertical bars with something else. In that case, I'd use
    s/\|[^|]*\|([^|]*)$/|976|$1/;