-1

I have a find_replace subroutine that takes in parameters (content, find regex, replace regex, result variable to store result in). I use qr// to pass both the find and replace regex. Find regex works well, but the replace regex doesn't.

$text = "11 22 33 44 55";
find_replace($text,qr/(33 )(.*?)( 55)/,qr/$1<BOLD>$2<\/BOLD>$3/,my $newText);
print $newText;
     #Should print: 11 22 <I>33 <B>44</B> 55</I>
     #But it prints: 11 22 <I>(?^:<B></B>)</I>

sub find_replace {
    my $content = shift;
    my $findRegEx = shift;
    my $replaceRegEx = shift;
    my $newVariable = \shift;

    ${$newVariable} = $content;
    ${$newVariable} =~ s/$findRegEx/<I>$replaceRegEx<\/I>/g;
}

I've found a fix for this to work:

$text = "11 22 33 44 55";
find_replace($text,qr/(33 )(.*?)( 55)/,sub{"$1<B>$2<\/B>$3"},my $newText);
print $newText;
     #Prints: 11 22 <I>33 <B>44</B> 55</I>

sub find_replace {
    my $content = shift;
    my $findRegEx = shift;
    my $replaceRegEx = shift;
    my $newVariable = \shift;

    ${$newVariable} = $content;
    ${$newVariable} =~ s($findRegEx){ "<I>".$replaceRegEx->()."<\/I>" }ge;
}

But this fix uses sub{"…"} instead of qr/…/. What I'm looking for is to keep using the qr// for passing the replace regex and get the same result.

Omar
  • 6,681
  • 5
  • 21
  • 36
  • 4
    The replacement part *isn't a regex*. Don't try to make it one. the sub{} way is the correct way to do this. – ysth Jan 29 '17 at 21:13
  • See [this post](http://stackoverflow.com/a/41280344/4653379) (for example) for what kinds of things you'd need to do. Use the `sub` approach you discovered, that's a nice way to work with regex :) – zdim Jan 29 '17 at 21:34
  • @zdim That code could be simplified a lot by using [`Data::Munge::replace`](https://metacpan.org/pod/Data::Munge#replace-STRING,-REGEX,-REPLACEMENT,-FLAG). – melpomene Jan 29 '17 at 21:36
  • @melpomene Look verys cool :) Thank you -- it's hard to know of what is out there! I'll add this to that post, along with `String::Substitution` note. (This may be even more to the point, need to play with it a little first.) – zdim Jan 29 '17 at 21:41

2 Answers2

3

First of all, using qr// for the replacement expression makes no sense since the replacement expression is not a regex pattern. qq// (double-quoted string) and q// (single-quoted string) are used to construct strings.

The main problem is that

s/$findRegEx/<I>$replaceRegEx<\/I>/g

means

my $repl_expr = sub { qq/<I>$replaceRegEx<\/I>/ };  # Interpolates $replaceRegEx
s/$findRegEx/ $repl_expr->() /eg

but you really want

my $repl_expr = sub { qq/$1<B>$2<\/B>$3/ };         # Interpolates $1, $2 and $3
s/$findRegEx/ $repl_expr->() /eg

If you need to start from a string (e.g. the replacement expression comes from a config file), then you can achieve what you want with the following:

use String::Substitution qw( gsub_copy );

my $newText = gsub_copy($text, qr/(33 )(.*?)( 55)/, '$1<BOLD>$2</BOLD>$3');
ikegami
  • 367,544
  • 15
  • 269
  • 518
1

You have to understand that the text in the replace is not a regex, but a string, ready to be interpolated. So you will have to do things like your workaround in order to delay interpolation until the regex has run against the required string.

That being said, if your sub is as simple as this, I'm not sure you really should put it in a sub - the s operator already is a sub call, and you're just complicating things. There are situations, of course, where you need to pass in a regex or whatever to a far more complex sub, and that could be a good thing, but you'd still need this type of delayed interpolation for it to work.

Tanktalus
  • 21,664
  • 5
  • 41
  • 68