php 贪婪和懒惰
Regex 十分强大。甚至有时候过于强大。例如,考虑当 regex ".*"
被应用到字符串 “The author of 'Wicked' also wrote 'Mirror, Mirror.'” 上时发生的情况。虽然预期 preg_match()
可能返回两个匹配,但是您可能会惊讶地发现只有一个结果:'Wicked' also wrote 'Mirror, Mirror.'
原因是什么?除非进行指定,否则诸如 *
(无或多个)和 +
(一个或多个)之类的操作符都很贪婪。如果模式可以继续匹配,那么它可能将生成最多的结果。要使匹配最少,则必须强制使某些操作符变得懒惰。懒惰操作将查找最短的匹配,然后就停止。要使操作符变得懒惰,请添加问号后缀。清单 6 显示了一个示例。
$text = 'The author of "Wicked" also wrote "Mirror, Mirror."';
if ( preg_match_all( '/".*?"/', $text, $matches ) ) {
print_r( $matches);
}
结果: Array ( [0] => Array ( [0] => "Wicked" [1] => "Mirror, Mirror." ) )
非贪婪模式匹配的原则是:
在可配也可不配的情况下, 优先不匹配. 记录备选状态, 并将匹配控制交给正则表达式的下一个匹配字符, 当之后的匹配失败的时候, 再回溯, 进行匹配。
贪婪模式匹配的原则是:
在可配也可不配的情况下, 优先匹配,直到不能匹配成功的情况下,记录备选状态,并把匹配控制交给正则表达式的下一个匹配字符,当之后的匹配失败的时候,再回溯,进行匹配。
例子:
比如对于字符串为:aaabb
1.如果正则表达式为:“.*?b” ,该匹配为非贪婪性匹配(其标志非贪婪的标识为量数元字符后面加? ,比如 +?、*?、??)、
非贪婪性模式匹配过程为:
匹配过程开始的时候, “.*?”首先取得匹配控制权, 因为是非贪婪模式, 所以优先不匹配, 将匹配控制交给下一个匹配字符”b”, “b”在源字符串位置1匹配失败(“a”), 于是回溯, 将匹配控制交回给”.*?”, 这个时候, “.*?”匹配一个字符”a”, 并再次将控制权交给”b”, 如此反复, 最终得到匹配结果, 这个过程中一共发生了3次回溯.
匹配结果为:aaab
2.如果正则表达式为:“.*b”,该匹配为贪婪性匹配。
贪婪模式匹配过程为:
匹配开始的时候,“.*”首先取得匹配控制权, 因为是贪婪模式, 所以优先匹配,并尽可能匹配多的内容,匹配整个字符串”aaabb”,然后将匹配控制交给“b”,因为“b”不能匹配空内容,所以“.*”回溯到 aaab,然后再把控制权交给“b”,这个时候“b”刚好匹配上字符串b,匹配成功,返回结果。这个过程一个发生了1次回溯。
匹配结果为:aaabb
如果我们的需要匹配的字符串比较长,并且正则表达式的写法没注意回溯次数,有可能会导致几千几万次回溯,导致栈溢出而出core。
所以建议大家写正则的时候要谨慎,不光注重效果还要注重效率,否则很容易导致深度嵌套, 另外考虑到性能, 建议能用字符串处理尽量使用字符串处理代替.
另外一个更快的方法是使用一个字符类来匹配每个非大于字符到下一个大于字符:
preg_match('/(<[^>]*>)/', 'do <b>not</b> press the button', $match);
// $match[1] 是 '<b>'
preg_match_all ("#<[^>]+>(.*?)</[^>]+>#","<bb>example: </bb><div align=left>this is a test</div>",$out);
print_r($out);
Array | |
( | |
[0] => Array | |
( | |
[0] => <bb>example: </bb> | |
[1] => <div align=left>this is a test</div> | |
) | |
[1] => Array | |
( | |
[0] => example: | |
[1] => this is a test | |
) | |
) |