PHP - 为什么我被警告我的正则表达式太大了?

Jac*_*unt 6 php regex

我想使用正则表达式来验证用户输入.我想允许字母,数字,空格,逗号,撇号,句号,感叹号和问号的任意组合,但我还想将输入限制为4000个字符.我想出了以下正则表达式来实现这一点:/^([a-z]|[0-9]| |,|'|\.|!|\?){1,4000}$/i.

但是,当我尝试使用这个正则表达式在PHP中使用preg_match()测试一个主题时,我会收到一个警告:PHP Warning: preg_match(): Compilation failed: regular expression is too large at offset 37并且主题无法测试.

我发现这很奇怪,因为如果我使用无限量词,测试通过就好了(我在下面演示了这种情况).

为什么将重复限制在4000个问题,但无限重复?

正则表达式-test.php的:

<?php

$infinite = "/^([a-z]|[0-9]| |,|'|\.|!|\?)*$/i";        // Allows infinite repetition
$fourk    = "/^([a-z]|[0-9]| |,|'|\.|!|\?){1,4000}$/i"; // Limits repetition to 4000

$string   = "I like apples.";

if ( preg_match($infinite, $string) ){

    echo "Passed infinite repetition. \n";
}

if ( preg_match($fourk, $string) ){

    echo "Passed maximum repetition of 4000. \n";
}

?>
Run Code Online (Sandbox Code Playgroud)

回声:

Passed infinite repetition 
PHP Warning:  preg_match(): Compilation failed: regular expression is too large at offset 37 in regex-test.php on line 16
Run Code Online (Sandbox Code Playgroud)

Mar*_*ano 7

该错误是由于它的LINK_SIZE偏移值将编译的模式大小限制为64K.这是一种预期的行为,如下所述,并不是因为重复的限制以及编译时如何解释模式.


在这种情况下

正如Alan Moore在他的回答中指出的那样,所有角色都应该在同一个角色类中.我更激烈,所以请允许我说这种模式是这样错了,它令我生厌.
- 没有进攻,我们大多数人也曾尝试过.这只是一种强调,不应该使用这种结构的尝试.

这里有3个常见的陷阱(x|y|z){1,4000}:

  1. 捕获子模式只应在需要时使用(存储匹配文本的特定部分,以便提取该值或在反向引用中使用它).对于所有其他用例,请坚持使用非捕获组原子组.它们表现更好,节省内存.
  2. 不应重复捕获子模式,因为最后一次重复会覆盖捕获的文本.
    -OK,它可能只能用于非常特殊的情况.
  3. 交替(使用|s)增加了回溯状态.尝试尽可能减少它们是一种很好的做法.在这种情况下,正则表达式^[ !',.0-9?A-Z]{1,4000}$/i将完全相同,不仅可以避免错误,还可以提供更好的性能.

LINK_SIZE

从" 处理非常大格局 "pcrebuild手册页:

在编译模式中,偏移值用于从一个部分指向另一个部分(例如,从左括号到交替元字符).默认情况下,在8位和16位库中,两个字节的值用于这些偏移,从而导致大约64K的编译模式的最大大小.

这意味着对于组的每次重复,编译的模式为交替中的每个子模式存储偏移值.在这种情况下,偏移量不会为已编译模式的其余部分留下任何内存.

在PHP dist的pcre_internal.h中的注释中更清楚地表达出来:

PCRE默认将其编译代码中的偏移量保持为2字节数量(始终以big-endian顺序存储).例如,这些用于从子模式的开头到其替代项及其结尾的链接.每个偏移量使用2个字节会将编译的正则表达式的大小限制在64K左右,这对于几乎所有人来说都足够大了.


使用pcretest,我得到以下信息:

PCRE version 8.37 2015-04-28

/^([a-z]|[0-9]| |,|'|\.|!|\?){1,575}$/i
Failed: regular expression is too large at offset 36

/^([a-z]|[0-9]| |,|'|\.|!|\?){1,574}$/i
Memory allocation (code space): 65432
Run Code Online (Sandbox Code Playgroud)


关于PCRE中的其他大小限制,您可以查看我的这篇文章.


覆盖LINK_SIZEPHP中的默认值

如果我们有一个真正的理由使用一个巨大的模式,并且这种模式无法通过任何方式进一步简化,那么链接大小可能会增加.但是,您只能通过自己重新编译PHP来实现这一点(因此,从现在开始,您的代码将无法移植).它应该是最后的手段,只要别无选择.

也在pcre_internal.h注释:

宏由值控制LINK_SIZE.在config.h文件中默认为2 ,但可以通过-D在命令行上使用来覆盖.这在Unix系统上通过"configure"命令自动完成.

PCRE链接大小可以配置为3或4:

./configure -DLINK_SIZE=4
Run Code Online (Sandbox Code Playgroud)

但请记住,较长的偏移量需要额外的数据,并且会减慢对preg_*函数的所有调用.

如果您自己编译PHP,请参阅在Unix系统上安装在Windows上构建您自己的PHP.