我希望一些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
这是一个错误(看起来像我)吗?
答案 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)*+"