Perl variable disambiguation within regex

124 Views Asked by At

I want to ensure variables are interpolated as they are within perl regular expressions. Would rather avoid binding over to other variables in a cascate of "ifs" that would rarely need that variable throughout the code execution. How could I ensure I used the variable?

use strict;
use warnings FATAL => "all";

$_="botherther";

my $bo = "bother";
if (/^$bother$/) { # want to consider just $bo here!
  print("Undestood regex as /($bo)ther/.\n");
} else {
  print("Couldnt discern variable.\n");
}

my $bi = { bo => "the" };
if (/^bo$bi->{bo}[r]ther$/) { # now, $bi->{bo} only
  print("Discerned /bo($bi->{bo})[r]/\n");
} else {
  print("Couldnt discern variable.\n");
}

I'm not able to find a way to wrap the variables properly within the regex. Of course I could just my $bi_resolved = $bi->{bo} or fill the regex with null-thingies (like [] or ()), but this does not feel like a proper separator.

For clarity:

  1. I wanted to expand $bo into bother to get the /botherther/ string in the first match.
  2. I wanted to expand $bi->{bo} into the to get <bo><the><[r]ther>, again /botherther/ in the second match.
  3. Important noting, I don't care for the sake of this context, about the escapes \Q and \E, "I am assuming there's never metacharacters within the variables".

I have searched thru questions, read documentation, and couldn't find an answer for this. Wrapping in ${} didn't work for me (that's trying to dereference stuff). So, while searching I feel I am just barking at the wrong tree... It's simply unbelievable nobody ever needed to ask something like that around perlmonks or stackoverflow. I'm probably just looking for the wrong keywords here. :/

2

There are 2 best solutions below

8
On BEST ANSWER

To separate interpolated variable names from other text is usually
done like ${name}

So, a part of the code sample becomes

use strict;
use warnings;

$_="botherther";

my $bo = "bother";
if (/^${bo}ther$/) { # want to consider just $bo here!
  print("Undestood regex as /${bo}ther/.\n");
} else {
  print("Couldnt discern variable.\n");
}

A good way to test stuff is to put it into a qr// then print it out :

my $rx = qr/^${bo}ther$/;
print $rx;

Per @choroba :

As far as the regex goes, it looks like the variable can also be wrapped
in a group without modification and should cover all cases.
It's really just a parsing thing. If Perl can distinquish delimiters to get
a variable notation in a string, it will interpolate it.

Like (?:$bo) or (?:$bi->{bo})
but it will be wrapped inside a residual group.

3
On

There are four main ways:

  • Use /x ($bi{bo} [r] instead of $bi{bo}[r])
  • Use curlies around the variable name (${bo}ther instead of $bother)
  • Escape the next character ($bo\->[0] instead of $bo->[0])
  • Isolation via parens or some other means ((?:$bi{bo})[r] instead of $bi{bo}[r])[1]

That said, if you're interpolating text (rather than a regex pattern), you should be using \Q$var\E anyway, making the problem moot.

use strict;
use warnings FATAL => "all";

$_="botherther";

my $bo = "bother";
if (/^\Q$bo\Ether$/) { # want to consider just $bo here!
  print("Understood regex as /^botherther\$/.\n");
} else {
  print("Couldn't discern variable.\n");
}

my $bi = { bo => "the" };
if (/^bo\Q$bi->{bo}\E[r]ther$/) { # now, $bi->{bo} only
  print("Discerned /^bothe[r]ther\$/\n");
} else {
  print("Couldn't discern variable.\n");
}

Thanks to @ysth for improvements.


  1. Imposes a small run-time penalty.