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

Monks,
I have a problem with a regex that contains interpolated patterns. I would expect the following pattern match
$string = 'string with word foo in it'; $string =~ s/(foo)/\1bar/g;
to produce the output

string with word foobar in it

which it happily does. But, I would like the "find" and "replace" elements of the regex to be interpolated into the pattern as variables (so, say, they could be provided by user input). For example
$string = 'string with word foo in it'; $find = '(foo)'; $replace = '\1bar'; $string =~ s/$find/$replace/g;
However this is instead giving me the output

string with word \1bar in it.

Any ideas what I can do to remedy this - I do not want to build up a string and then use eval on it.

Many Thanks

Replies are listed 'Best First'.
Re: Regex prob with interpolated patterns
by halley (Prior) on Apr 06, 2004 at 17:29 UTC
    Use the \1 form inside a regex, to refer to previously collected groups.

    Use the $1 form anywhere else, including the right half of a s/// operator.

    In your case, you will have to eval the results, but that's pretty easy: express the right hand as an expression, and add the /e option twice to your s/// operator. (This will require careful attention to tainting if you're security-conscious.)

    --
    [ e d @ h a l l e y . c c ]

Re: Regex prob with interpolated patterns
by Roy Johnson (Monsignor) on Apr 06, 2004 at 17:32 UTC
    Backslash-backreferences aren't cricket in the replacement portion of s///. You would want '$1bar', and then you'd need to use the /ee flag to get it to eval properly: once to turn $bar into $1foo and once to sub for $1. But $1foo isn't a proper expression, you need to put a . to concatenate them, or double-quotes around it:
    $replace = '$1.bar'; # or '"$1bar"' s/$find/$replace/eeg;
    Update: It might be more secure to put the quotes in the s/// expression itself:
    $replace = 'warn "AARGH!"'; s/$find/qq(qq($replace))/eeg;

    The PerlMonk tr/// Advocate
Re: Regex prob with interpolated patterns
by Enlil (Parson) on Apr 06, 2004 at 17:33 UTC
Re: Regex prob with interpolated patterns
by Paladin (Vicar) on Apr 06, 2004 at 17:33 UTC
    1. You shouldn't use \1 on the RHS of s///, you should be using $1. \1 is used in LHS of s/// in the regex itself.
    2. Take a look at the /e modifier for s///. Your code will look something like this:
    $string = 'string with word foo in it'; $find = '(foo)'; $replace = '"${1}bar"'; $string =~ s/$find/$replace/eeg;
    Note the double quotes in $replace and the /ee on the s///. The first e causes $replace to be evaluated, which returns "${1}bar" which is then evaluated by the second e which returns foobar.