【问题标题】:Stop runaway regular expression停止失控的正则表达式
【发布时间】:2014-05-29 15:30:25
【问题描述】:

有没有办法阻止失控的正则表达式?

我对如何修改它的建议不感兴趣。我知道可以对其进行修改,使其不会中断等,但我正在针对数千个输入运行单个正则表达式,因此修改它意味着我需要在 所有 输入上重新测试它。不太实用。

所以确切的问题是:我可以使用某种形式的计时器来终止需要超过 X 秒才能完成的正则表达式吗?

【问题讨论】:

  • 根据我自己的测试向我揭示的情况,您实际上无法从正则表达式引擎中惊慌失措。但是,有一个模块:Sys::SigAction,它有一个函数“timeout_call”,它将在进行中中断正则表达式引擎。但是,中断正在进行的 RE 引擎是不安全的,并且可能导致 seg-fault。
  • @DavidO 那么简单的分叉就可以了。
  • @mpapec ...通过“简单”的一些定义。 ;) 是的,fork 有时是一个不错的选择。我将在下面更新我的答案以将其作为建议包含在内。
  • @mpapec :不,我认为 fork/alarm 的建议非常合理。太糟糕了,这个问题即将被关闭(希望不会成功),因为这不是一个坏问题。该用例的价值可能值得怀疑,但在某些合理的情况下,时间受限的匹配是合理的。顺便说一句:我认为线程在 Perl-5.20 中已被弃用。
  • @DavidO 回复:threads have been deprecated in Perl-5.20 世界即将结束。 :)

标签: regex perl


【解决方案1】:

Perl 的内置 alarm 不足以打破长期运行的正则表达式,因为 Perl 不会在内部操作码内提供警报超时的机会。 alarm 根本无法穿透它。

在某些情况下,最明显的解决方案是 fork 一个子进程,并在它运行时间过长后使用 alarm 将其超时。这篇 PerlMonks 帖子演示了如何使分叉进程超时:Re: Timeout on script

在 CPAN 上有一个名为 Sys::SigAction 的 Perl 模块,它有一个名为 timeout_call 的函数,它使用不安全的信号中断一个长时间运行的正则表达式。但是,RE 引擎并非设计为中断,并且可能会处于不稳定状态,这可能导致大约 10% 的时间段发生故障。

下面是一些示例代码,展示了 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 方法可能是确保您保持控制的最安全方法。

顺便说一句,这个问题和我的答案的一个版本被交叉发布到PerlMonks

【讨论】:

    猜你喜欢
    • 2014-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多