有没有办法阻止失控的正则表达式?
我对如何修改它的建议不感兴趣。我知道它可以被修改,所以它不会破坏等,但我正在运行一个针对数千个输入的正则表达式,所以修改它意味着我需要在所有输入上重新测试它。不太实际。
所以确切的问题是:是否有某种形式的计时器可用于终止需要超过X秒才能完成的正则表达式?
答案 0 :(得分:10)
Perl的内置alarm
不足以打破长时间运行的正则表达式,因为Perl没有为内部操作码内的警报超时提供机会。 alarm
根本无法穿透它。
在某些情况下,最明显的解决方案是fork
一个子流程,并在使用alarm
进行太长时间之后将其计时。此PerlMonks帖子演示了如何超时分叉流程:Re: Timeout on script
CPAN上有一个名为Sys::SigAction的Perl模块,它有一个名为timeout_call
的函数,将使用不安全信号中断长时间运行的正则表达式。然而,RE引擎并未被设计为中断,并且可能处于不稳定状态,这可能导致大约10%的时间出现seg-fault。
下面是一些示例代码,演示了Sys :: SigAction成功突破正则表达式引擎,以及证明Perl的alarm
无法执行此操作:
use Sys::SigAction 'timeout_call';
use Time::HiRes;
sub run_re {
my $string = ('a' x 64 ) . 'b';
if( $string =~ m/(a*a*a*a*a*a*a*a*a*a*a*a*)*[^Bb]$/ ) {
print "Whoops!\n";
}
else {
print "Ok!\n";
}
}
print "Sys::SigAction::timeout_call:\n";
my $t = time();
timeout_call(2,\&run_re);
print time() - $t, " seconds.\n";
print "alarm:\n";
$t = time();
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
alarm 2;
run_re();
alarm 0;
};
if( $@ ) {
die unless $@ eq "alarm\n";
}
else {
print time() - $t, " seconds.\n";
}
输出将是:
$ ./mytest.pl
Sys::SigAction::timeout_call:
Complex regular subexpression recursion limit (32766) exceeded at ./mytest.pl line 11.
2 seconds.
alarm:
Complex regular subexpression recursion limit (32766) exceeded at ./mytest.pl line 11.
^C
你会注意到在第二个电话 - 那个应该与alarm
超时的电话中,我最终不得不ctrl-C
因为alarm
不足以打破退出RE引擎。
Sys :: SigAction的一个重要警告是即使它能够打破长时间运行的正则表达式,因为RE引擎不是为这种中断而设计的,整个过程会变得不稳定,导致段错误。虽然每次都不会发生,但它可能会发生。这可能不是你想要的。
我不知道您的正则表达式是什么样的,但如果它符合RE2 engine允许的语法,您可以使用Perl模块re::engine::RE2来处理RE2 C ++库。该引擎保证线性时间搜索,但它提供的功能不如Perl的内置引擎。 RE2方法通过提供线性时间保证首先避免了整个问题。
但是,如果你不能使用RE2(可能是因为你的正则表达式的语义要求太高),fork / alarm方法可能是确保你保持控制的最安全的方法。 / p>
(顺便说一下,这个问题和我的答案版本都被转发到PerlMonks。)