7

技术问题:

给定一个正则表达式:

my $regEx = qr{whatever$myVar}oxi; # Notice /o for "compile-once"

强制它按需重新编译的最有效方法是什么?(例如,当我从程序逻辑中知道$myVar值发生了变化时)没有丢弃/o并且依赖于 Perl 的内部智能来自动重新编译?

注意:正则表达式用于替换,这可能会影响重新编译规则 sans /o:

$string2 =~ s/$regEx//;

上下文是:

  • 我有一个正则表达式,它是通过从配置文件中插入相当长(> 1k 长)的字符串来构建的。

    • 该文件每 60 分钟重新读取一次。

    • 如果从文件中读取的字符串发生更改(通过更改文件时间戳定义$myVar),我想使用.

  • 正则表达式在 mod_perl 下运行的 Perl 模块中被反复频繁地使用。

    • 这意味着(加上字符串长度 > 1-2k)我必须使用 " /o" 修饰符在正则表达式上强制编译一次,以避免 Perl 反复检查变量值是否改变的性能损失(这个启发式是从perlop qr//,因为正则表达式用作如上s///所示的一部分,而不是单独用作匹配项)。

    • 这反过来意味着,当我知道变量在 1 小时内重新 slurping 后发生了变化时,我需要强制正则表达式重新编译,尽管有/o修饰符。

更新:这是我需要的说明/o- 没有它,每次循环迭代都会重新编译(因此必须检查)正则表达式;有了它,它不是:

$ 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
4

3 回答 3

4
当我从程序逻辑中知道 $myVar 值发生了变化时

m//,s///并且qr// 只有在模式没有改变的情况下才编译。要获得您请求的行为,您所要做的就是删除/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"

所以,

如果从文件中读取的字符串发生更改(通过更改文件时间戳来定义),我想使用 $myVar 中的 re-slurped 字符串值重新编译正则表达式。
my $new_myVar = ...;
if ($myVar ne $new_myVar) {
   $re = qr/$new_myVar/;
   $myVar = $new_myVar;
}
...
s/$re/.../

要不就

$myVar = ...;
...
s/$myVar/.../
于 2011-06-01T20:30:03.273 回答
3

你基本上回答了你自己的问题。用于qr{...}创建已编译的正则表达式对象,然后使用它:

my $re = qr{...};

...

if ($str =~ $re) {
   # this used the statically compiled object
}

...

if ($time_to_recompile) {
    $re = qr{...};
}

您甚至不需要“/o”修饰符。

于 2011-06-01T18:42:57.607 回答
2

根据 perlop

'o' 修饰符的效果不会传播,仅限于那些显式使用它的模式。

所以如果你写

my $str = 'x';
my $re  = qr/$str/o;
...
if (s/$re//) {
    ...
}

Perl 仍然会$re在执行s///. 作为/o一个承诺,$str在编译中使用的值$re不会改变,所以如果你重新执行qr//你会得到相同的结果,即使$str已经改变了。您可以通过以下方式看到这一点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//; 
}

使用/o修饰符,您只会在“Setting $re”第一次通过循环后看到“Compiling REx...”。没有它,您将在每次迭代中看到它。

要点是,如果您想在运行时更改模式,则不应使用/o. 它不会影响,s///并且会阻止您在需要时重新编译$re

于 2011-06-01T20:56:28.267 回答