最新的Perl不会匹配超过32768个字符的某些正则表达式

时间:2014-10-07 00:01:54

标签: regex perl

我希望一些Perl大师能够对以下内容发表意见。这是我能找到的最小的例子,可以重现我的问题:

>./perl -e 'print (("a".("f"x32767)."a") =~ /a(?:[^a]|bb)*a/)'
1

>./perl -e 'print (("a".("f"x32768)."a") =~ /a(?:[^a]|bb)*a/)'
>

我确实从源代码编译了最新的Perl,只是为了看看它是否能解决这个问题:

>./perl -v

This is perl 5, version 20, subversion 1 (v5.20.1) built for i686-linux

这是一个错误(看起来像我)吗?

2 个答案:

答案 0 :(得分:10)

添加use warnings

use strict;
use warnings;
use feature qw(say);

say "Version is $^V";

say +("a".("f"x32767)."a") =~ /a(?:[^a]|bb)*a/ ? 'matches' : 'no match';

say +("a".("f"x32768)."a") =~ /a(?:[^a]|bb)*a/ ? 'matches' : 'no match';

输出:

Complex regular subexpression recursion limit (32766) exceeded at e.pl line 7.
Complex regular subexpression recursion limit (32766) exceeded at e.pl line 9.
Version is v5.20.1
matches
no match

来自perldiag

  

超出复杂的常规子表达式递归限制(%d)

     

(W regexp)正则表达式引擎在需要反向跟踪的复杂情况下使用递归。递归深度限制为32766,或者在堆栈无法任意增长的架构中可能更少。 ("简单""中等"情况在没有递归的情况下处理,不受限制。)尝试缩短检查中的字符串;循环使用Perl代码(例如,使用while)而不是正则表达式引擎;或重写正则表达式,使其更简单或回溯更少。 (有关掌握正则表达式的信息,请参阅perlfaq2。)

答案 1 :(得分:9)

这是自2002年以来报告的known bug,尚未修复。您现在知道您不是第一个遇到此错误的人(或功能,您很快就会看到)。

从错误报告中的this comment开始,似乎量词(*+{n,m}{n,})设计为具有上限重复次数,当用于回溯的堆栈溢出时阻止引擎发生段错误,但是违反正则表达式中Kleene运算符的定义(重复任意次数的模式)并给出查询 1 的错误答案。

1 相比之下,Java的正则表达式引擎(Oracle的实现)只允许在这种情况下发生StackOverflowError,但量词的上限为2 32 - 1,足以满足大多数用例。对于这样的情况,存在一种解决方法,即使用占有量词。

同样的注释也会打印正则表达式编译调试信息,输出清楚地显示*已转换为{0,32767}。它在我的机器上也是可重现的(为x86_64-linux-thread-multi构建的perl v5.10.1(*))。

$ perl -Mre=debug -wce '/(A|B)*/'
Compiling REx "(A|B)*"
Final program:
   1: CURLYM[1] {0,32767} (15)
   5:   TRIE-EXACT[AB] (13)
        <A>
        <B>
  13:   SUCCEED (0)
  14: NOTHING (15)
  15: END (0)
minlen 0
-e syntax OK
Freeing REx: "(A|B)*"

以下测试进一步确认了问题,并显示perl不允许您指定超出限制的重复。

$ perl -e 'print (("a".("f"x32767)."a") =~ /a(?:[^a]|bb){0,32767}a/)'
Quantifier in {,} bigger than 32766 in regex; marked by <-- HERE in m/a(?:[^a]|bb){ <-- HERE 0,32767}a/ at -e line 1.

使量词占有性*+确实解决问题,因为限制仍然存在:

$ perl -Mre=debug -wce '/(A|B)*+/'
Compiling REx "(A|B)*+"
Final program:
   1: SUSPEND (19)
   3:   CURLYM[1] {0,32767} (17)
   7:     TRIE-EXACT[AB] (15)
          <A>
          <B>
  15:     SUCCEED (0)
  16:   NOTHING (17)
  17:   SUCCEED (0)
  18: TAIL (19)
  19: END (0)
minlen 0
-e syntax OK
Freeing REx: "(A|B)*+"