使用两个匹配时,preg_match似乎达到了极限

edw*_*rkf 5 php regex

我遇到了一个奇怪的问题.当我尝试使用php-5.3.3使用两个匹配时,看起来我正在使用preg_replace达到某种限制

// works fine
$pattern_1 = '?START(.*)STOP?';
$string = 'START' . str_repeat('x',9999999) . 'STOP' ;
preg_match($pattern_1, $string , $matchedArray )        ;

$pattern_2 = '?START-ONE(.*)STOP-ONE.*START-TWO(.*)STOP-TWO.*?';

// works fine
$string = 'START-ONE this is head stuff STOP-ONE  START-TWO' . str_repeat('x', 49970) . 'STOP-TWO' ;
preg_match($pattern_2, $string , $matchedArray_2 )      ;

// didnt work
$string = 'START-ONE this is head stuff STOP-ONE  START-TWO' . str_repeat('x', 49971) . 'STOP-TWO' ;
preg_match($pattern_2, $string , $matchedArray_3 )      ;
Run Code Online (Sandbox Code Playgroud)

只有一个匹配的第一个选项使用非常大的字符串并且没有问题.

第二个选项的字符串长度为50,026,工作正常.最后一个选项的字符串长度为50,027(一个),匹配不再有效.由于49971号码在发生错误时可能会有所不同,因此可以将其更改为更大的值以模拟问题.

任何想法或想法?也许这是一个php版本的问题?也许一个可能的解决方法只是只使用一个匹配而不是两个,然后运行preg_match两次?

Luc*_*ski 4

好吧,PHP 对于正则表达式错误不是很关心,它只是返回最后一种情况,根据PHP 文档false,它只是告诉发生了错误

preg_match在 C# 中使用 PCRE(使用的正则表达式引擎)重现了该问题(但字符数要高得多),我得到的错误是PCRE_ERROR_MATCHLIMIT

这意味着您达到了 PCRE 中设置的回溯限制。这只是防止引擎无限循环的安全措施,我认为您的 PHP 配置将其设置为较低的值。

要解决此问题,您可以为控制此限制的 PHP 选项设置更高的值pcre.backtrack_limit

ini_set("pcre.backtrack_limit", "10000000"); // Actually, this is PCRE's default
Run Code Online (Sandbox Code Playgroud)

附注:

  • 您可能应该替换(.*)为,(.*?)以减少无用的回溯并确保正确性(否则正则表达式引擎将越过该STOP字符串,并且必须回溯才能到达它)
  • 用作?模式分隔符是一个主意,因为它会阻止您使用?元字符并因此应用上述建议。实际上,您永远不应该使用正则表达式元字符作为模式分隔符。

如果您对更多底层细节感兴趣,请参阅 PCRE 文档的相关内容(重点是我的):

match_limit字段提供了一种方法,防止 PCRE 在运行不会匹配但在搜索树中具有大量可能性的模式时消耗大量资源。典型的示例是使用嵌套无限重复的模式。

在内部,pcre_exec()使用一个名为 的函数match(),它会重复调用该函数(有时是递归调用)。设置的限制match_limit是在比赛期间调用此函数的次数,这具有限制可能发生的回溯数量的效果。对于未锚定的模式,主题字符串中每个位置的计数从零重新开始。

pcre_exec()使用通过 JIT 选项成功研究的模式进行调用时,执行匹配的方式完全不同。但是,仍然存在持续很长时间的失控匹配的可能性,因此match_limit在这种情况下也使用该值(但以不同的方式)来限制匹配可以持续的时间。

限制的默认值可以在PCRE构建时设置;默认值是 1000 万,它可以处理除最极端情况之外的所有情况。pcre_exec()您可以通过提供一个pcre_extra在其中设置的块来覆盖默认值match_limit,并PCRE_EXTRA_MATCH_LIMIT在标志字段中设置。如果超出限制,pcre_exec()则返回PCRE_ERROR_MATCHLIMIT

匹配限制的值也可以由以下形式的模式开头的项目提供

 (*LIMIT_MATCH=d)
Run Code Online (Sandbox Code Playgroud)

其中d是十进制数。但是,除非 d 小于调用者设置的限制,否则此类设置将被忽略pcre_exec(),或者如果未设置此类限制,则小于默认值。