Technical question:
Given a regex:
my $regEx = qr{whatever$myVar}oxi; # Notice /o for "compile-once"
What is the most effective way to force it to recompile on demand? (e.g. when I know from the program logic that $myVar
value changed) without dropping /o
and depending on Perl's internal smarts to auto-recompile?
NOTE: The regex is used in a substitution, which may affect re-compilation rules sans /o:
$string2 =~ s/$regEx//;
The context is:
I have a regular expression that is built by slurping in a fairly long (>1k long) string from a config file.
That file is re-read once every 60 minutes.
If the string read from the file changes (as defined by changing file timestamp), I want to re-compile the regex using the re-slurped string value in $myVar
.
The regex is used repeatedly and frequently in the Perl module running under mod_perl.
This means that (coupled with the string being >1-2k long) I must use "/o
" modifier to force compile-once on the regex, to avoid performance hit of Perl repeatedly checking if the variable value changed (this heuristic is from perlop qr//
, since the regex is used as part of s///
as shown above and not by itself as a match).
That in turn means that, when I know that the variable changed after re-slurping it in 1 hour, I need to force the regex to re-compile despite the /o
modifier.
UPDATE: Here's an illustration of why I need /o
- without it, the regex is recompiled (and thus necessarily checked) every loop iteration; with it it is NOT:
$ perl -e '{for (my $i=0; $i<3; $i++) {
my $re = qr{$i}oix; $s="123"; $s =~ s/$re//;
print "i=$i; s=$s\n"; }}'
i=0; s=123
i=1; s=123
i=2; s=123
$ perl -e '{ for (my $i=0; $i<3; $i++) {
my $re = qr{$i}ix; $s="123"; $s =~ s/$re//;
print "i=$i; s=$s\n"; }}'
i=0; s=123
i=1; s=23
i=2; s=13
when I know from the program logic that $myVar value changed
m//
, s///
and qr//
only compile if the pattern doesn't change. All you have to do to get the behaviour you requested is to remove the /o
.
$ perl -Mre=debug -e'
qr/$_/ for qw( abc abc def def abc abc );
' 2>&1 | grep Compiling
Compiling REx "abc"
Compiling REx "def"
Compiling REx "abc"
Therefore,
If the string read from the file changes (as defined by changing file timestamp), I want to re-compile the regex using the re-slurped string value in $myVar.
my $new_myVar = ...;
if ($myVar ne $new_myVar) {
$re = qr/$new_myVar/;
$myVar = $new_myVar;
}
...
s/$re/.../
or just
$myVar = ...;
...
s/$myVar/.../
You basically answered your own question. Use qr{...}
to create a compiled regexp object and then use it:
my $re = qr{...};
...
if ($str =~ $re) {
# this used the statically compiled object
}
...
if ($time_to_recompile) {
$re = qr{...};
}
You do not even need the "/o" modifier.
According to perlop
The effect the 'o' modifier has is not propagated, being restricted to those patterns explicitly using it.
So if you write
my $str = 'x';
my $re = qr/$str/o;
...
if (s/$re//) {
...
}
Perl will still check to see whether or not $re
has changed when executing the s///
. The /o
acts as a promise that the value of $str
used in the compilation of $re
won't change so if you re-executed the qr//
you'd get the same result even if $str
has changed. You can see this in effect with use re 'debug'
:
use strict;
use warnings;
use re 'debug';
foreach my $i (0 .. 2) {
my $s = '123';
print STDERR "Setting \$re\n";
my $re = qr/$i/o;
print STDERR "Performing s///\n";
$s =~ s/$re//;
}
With the /o
modifier, you'll only see "Compiling REx..." after "Setting $re" the first time through the loop. Without it you'll see it each iteration.
The take-away is that if you want to change the pattern during runtime you shouldn't use /o
. It won't affect the s///
and it will prevent you from being able to re-compile $re
when you need to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With