Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Regular expression "replace string interpolation" problem

by cLive ;-) (Parson)
on May 16, 2008 at 22:08 UTC ( #687031=perlquestion: print w/ replies, xml ) Need Help??
cLive ;-) has asked for the wisdom of the Perl Monks concerning the following question:

For tidiness, I want to store a bunch of regular expressions outside of a script, but I can't work out how to get interpolation of the replace string correct. Here's a simple example:

#!/usr/bin/perl use strict; use warnings; # works as expected my $text = 'Hello world'; $text=~ s/(\w+)/$1 Perl/; print "Regular regex: $text\n"; # but now try with variable interpolation my $text2 = 'Hello world'; my $match = qr/(\w+)/; my $replace = '$1 Perl'; $text2 =~ s/$match/$replace/e; # not what I want print "Interpolated regex: $text2\n";

I won't bore you with all the variations I've tried so far, but I've been playing with this for an hour (including qr and different modifiers) and it's driving me crazy. Any ideas?

Comment on Regular expression "replace string interpolation" problem
Download Code
Re: Regular expression "replace string interpolation" problem
by pc88mxer (Vicar) on May 16, 2008 at 22:21 UTC
    This should work:
    ... my $replace = '"$1 Perl"'; $text2 =~ s/$match/$replace/ee;
    Or you might want to write it like this:
    my $replace = '$1 Perl'; # use original setting of $replace $text2 =~ s/$match/'"'.$replace.'"'/ee;
    Probably even a better solution would be write $replace as a subroutine:
    my $replace = sub { "$1 Perl" }; $text2 =~ s/$match/$replace->()/e;
    and then only level of evaluation is needed.

      That works - thanks :)

      But, reading through the regex documentation, I still can't work out exactly why. Hmmm.

      so I guess needs to be \\" in the replace string for quotes. Arghhh, double escaping!!

        Or how to embed " in the output - but that's not a biggie right now.

        You want to produce the code
        "$1 \"Perl\""
        so
        my $replace = '"$1 \\"Perl\\""'; # Produces: "$1 \"Perl\"" s/$match/eval $replace/e

        Technically, the slashes don't need to be doubled since single quotes are forgiving when it's unambiguous.

        To explain what's going on it might be helpful to use underlined bold text to represent strings. So, one can write:
        eval 2+24
        and this means if you eval the string 2+2, you'll get the string 4. In perl this is just:
        my $x = '2+2'; my $y = eval $x; # $y is the string '4'

        In your original example, what is happening during the substitution s/$match/$replace/e is:

        eval $replace$1 Perl
        I.e., the match is replaced with the string $1 Perl, and that is why $text2 contains $1 Perl world.

        How about just adding another /e modifier to evaluate the substitution again? Unfortunately this doesn't work because $1 Perl world is not valid perl syntax:

        eval eval $replaceeval $1 Perl worldsyntax error

        When $replace and the substitution is written as:

        my $replace = '"$1 Perl"'; $text2 =~ s/$match/$replace/ee;
        the evaluation of the replacement proceeds as follows:
        eval eval $replaceeval "$1 Perl"
        and because of the double quotes this last eval yields $1 concatenated with a space and the string Perl.
Re: Regular expression "replace string interpolation" problem
by ikegami (Pope) on May 16, 2008 at 22:27 UTC

    /e treats the contents of the replace operand of the s/// operator as Perl source code. Therefore, $replace (not its content) is treated as source code. An expression consisting of a variable name returns its contents, so s/.../$replace/e replaces the match with the contents of $replace.

    To execute Perl code in a variable, you need eval EXPR. Change
    s/$match/$replace/e
    to
    s/$match/eval qq{"$replace"}/e

    The latter can also be written as
    s/$match/qq{"$replace"}/ee
    (note the double "e") but I find that to be rather obfuscated. eval EXPR is dangerous so it shouldn't be hidden.

    Update: Added missing code to add quotes to the code to execute.

Re: Regular expression "replace string interpolation" problem
by pc88mxer (Vicar) on May 17, 2008 at 07:09 UTC
    What is likely confusing is that both of these statements yield the same result:
    $text2 =~ s/$match/$replace/; $text2 =~ s/$match/$replace/e;
    The evaluation done in the second substitution replaces the variable interpolation that is taking place in the first substitution.
      I used this and it works my $replace = "$1 Perl";
Re: Regular expression "replace string interpolation" problem
by JEB (Novice) on Oct 24, 2010 at 16:11 UTC

    What about this replace text, which attempts to lower case the protocol and hostname in a URL string, but leave the path (and anything else) as is:

    my $url = 'http://www.FOo.COm/wibbLE'; my $search = '^([^:]+://[^/]+)/?(.*)?$'; my $replace = '\L$1\E/$2'; printf "%s\n", $url if ($url =~ s!$search!$replace!);

    Anyone got a way of not resorting to "/e" or "/ee" or eval to make this work? For me the $replace is coming from a database, and I don't trust the code so I dont want it doing an "unlink /" or similar! :)

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (6)
As of 2014-10-25 13:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (143 votes), past polls