使用preg_match解析字符串时出错

时间:2014-03-31 23:18:52

标签: php regex preg-match

我有一个方法可以检查字符串是否包含特殊的unicode字符。我在这里找到了正则表达式http://w3.org/International/questions/qa-forms-utf-8.html。但我发现它不适用于超过307个字符的字符串。以下是示例代码:

$regex = '%^(?:
      [\x09\x0A\x0D\x20-\x7E]            # ASCII
    | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
    | \xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
    | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
    | \xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
    | \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
    | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
    | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
    )*$%xs';

$matches = null;
if (preg_match($regex,
    '..........<p>
...............<a href="#tag"></a><br>
...............<a href="#tag"></a><br>
...............<a href="#tag"></a><br>
...............<a href="#tag"></a><br>
...............<a href="#tag"></a><br>
...............<a href="#tag"></a><br>
...............<a href="#tag"></a><br>
.........</p>', $matches)) {
    echo 'IN';
} else {
    echo 'OUT!';
}

我为测试添加了点而不是空格。当我运行这个脚本时,我得不到服务器的响应(尽管我已经设置了所有错误,但我甚至没有错误)。但是,如果我只从匹配的字符串中删除一个字符,它将按预期工作(IN被回显)。我似乎无法在网上找到任何可以帮助我的东西。调试没有用,因为它只是在if停止(中断)(调试会话停止)。

以下是我在php.ini中的pcre设置:pcre.backtrack_limit:1000000,pcre.recursion_limit:100000

我已经尝试过在线工具检查正则表达式而且没有产生这个错误(它工作得很好)。

任何?感谢。

1 个答案:

答案 0 :(得分:2)

原因似乎是服务器功能上限的bactracking限制(这就是您没有收到错误消息的原因)。

您可以使用以下方式限制回溯:

$regex = '%^(?>
      [\x09\x0A\x0D\x20-\x7E]++                # ASCII
    | (?>[\xC2-\xDF][\x80-\xBF])++             # non-overlong 2-byte
    | (?>\xE0[\xA0-\xBF][\x80-\xBF])++         # excluding overlongs
    | (?>[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2})++  # straight 3-byte
    | (?>\xED[\x80-\x9F][\x80-\xBF])++         # excluding surrogates
    | (?>\xF0[\x90-\xBF][\x80-\xBF]{2})++      # planes 1-3
    | (?>[\xF1-\xF3][\x80-\xBF]{3})++          # planes 4-15
    | (?>\xF4[\x80-\x8F][\x80-\xBF]{2})++      # plane 16
    )*+$%xs';

关于回溯,原子组和占有量词:

回溯是一种机制,当子模式在字符串中的位置失败时,正则表达式引擎用于探索字符串中位置匹配的其他可能性。

让我们考虑字符串aaabcccb和模式^.+cb$

 string     |  pattern   |   state
------------+------------+--------------------------
 aaabcccb   |  ^.+cb$    |   BEGIN
 aaabcccb   |  ^.+cb$    |   OK
 aaabcccb   |  ^.+cb$    |   FAIL
 aaabcccb   |  ^.+cb$    |   BACKTRACK
 aaabcccb   |  ^.+cb$    |   FAIL
 aaabcccb   |  ^.+cb$    |   BACKTRACK
 aaabcccb   |  ^.+cb$    |   OK
 aaabcccb   |  ^.+cb$    |   OK
 aaabcccb   |  ^.+cb$    |   OK, SUCCEED
------------+------------+--------------------------

这描述了正则表达式引擎的默认行为,贪婪量词.+的子模式采用了所有可能的(本例中的所有字符串),但在正则表达式引擎必须逐个字符后返回使子模式cb成功。贪婪的量词允许这种行为,并可能让人物回来。

您可以使用占有量词来禁止回溯。 ^.++cb$的示例:

 string     |  pattern   |   state
------------+------------+--------------------------
 aaabcccb   |  ^.++cb$   |   BEGIN
 aaabcccb   |  ^.++cb$   |   OK
 aaabcccb   |  ^.++cb$   |   FAIL
 aaabcccb   |  ^.++cb$   |   NO MATCH
------------+------------+--------------------------

正则表达式引擎无法在.++匹配的子字符串中回溯,整个模式立即失败,因为找不到c

原子组定义了一个子模式,其中不允许正则表达式引擎回溯。换句话说,所有格量词和原子组是相同的特征:(?>a+)&lt; =&gt; a++

注意:但是,请记住,只要未关闭,正则表达式引擎就可以在原子组内回溯:^(?>.+c)b$将使用先前字符串成功,但^(?>.+)cb$将失败。

一旦原子组被关闭,或者当你使用占有量词时,匹配的子串就是词源意义上的原子(即无法分割的东西)。但是,正则表达式引擎总是可以逐个原子地回溯,例如:^(?>ab)+abc$(或abababc)失败时,^(?>ab)++abc$将匹配^(?>(?>ab)+)abc$

原子群和占有量词的主要优势之一(或禁止使用bactracking的事实)是减少使模式成功或失败的步骤数。

<强>改进:

由于占有量词和原子组在任何地方都使用,每个子串都是一劳永逸地匹配,当一个字符不在其中一个组中时,该模式将立即失败。

另一项改进是为交替的每个元素添加量词。字符串示例:zzzzzzzzzzzzza

使用模式:(?:a|b|c|...x|y|z)+

正则表达式引擎必须尝试交替的每个部分,直到它找到好的字母,并为每个字母(13x26 = 278个测试以获得所有z

使用模式:(?>a+|b+|c+|...x+|y+|z+)+

正则表达式引擎只需要26次测试,一旦到达z+,它就会获得所有z