一次性子组——迹忆客-ag捕鱼王app官网
一次性子组概念
之前我们介绍过贪婪模式,贪婪模式的遍历过程以及非贪婪模式的遍历过程。重复性量词是造成贪婪的原因。所谓贪婪就是尽可能多的去匹配,当下一个正则token匹配失败的时候,引擎会因为量词的贪婪性进行回溯匹配,重复多次的去回溯。这会造成性能的损失。所谓一次性子组
就是将量词所要重复的正则作为一个子组,并且只要是量词后的token匹配失败了,那整个子组就不再进行回溯,在没有可选路径的情况下,整个正则宣告匹配失败。一次性子组
用(?>
来表示。
下面我们先来看一个贪婪的例子,正则表达式/<(. )>/
匹配字符串 "hello worldtest"
。这个正则可以匹配出字符串"hello world"
。它的匹配过程在贪婪和量词
一节介绍过,这里我们简单回顾一下。首先token <
匹配字符串的第一个'<'
;然后是点号.
可以匹配任意字符,再加上
的贪婪性,所以由(. )
匹配到的是"span>hello worldtest"
。由于到了行尾,所以点号在默认情况下不能再匹配;正则中的下一个token >
无法匹配行尾,所以匹配失败,这时正则引擎知道
是贪婪的,所以开始回溯,此时(. )
匹配的是"span>hello worldtes"
,同样 token >
还是不能匹配字符't'
。正则引擎继续回溯,直到(. )
匹配的是"span>hello world,此时的token
>
和字符'>'
可以匹配成功,因此整个表达式匹配成功。我们看,在这个过程中正则引擎是回溯了很多次才正确匹配成功的。那如果我们在这个例子中使用一次性子组
,/<(?>. )>/
,<
正常匹配这没什么问题,(?>. )
可以匹配到行尾,token >
不能匹配行尾,所以匹配失败。由于前面是一次性子组,所以正则引擎不再进行回溯,因此整个正则就匹配失败了。
代码示例
hello worldtest";
$pattern = "/<(?>(. ))>/";
$res = preg_match($pattern,$str,$matches);
print_r($matches);
/*** 执行结果
array
(
)
*/
当然,上面的例子只是用来说明一次性子组的使用原理,实际情况中,我们当然是期望正则表达式要能匹配出我们想要的内容来了。
一次性子组
的特点决定了它的性能是很高的。一般情况下可以和后瞻断言
结合使用。还是上面字符串hello worldtest
,要匹配整个字符串,并且字符串须是以"test"
结尾。当然我们可以使用/.*test/
来匹配,但是上面我们说过在匹配test
的时候,正则引擎要回溯四个字符才能匹配上test
。假如目标字符串不是以"test"
结尾,那就要回溯整个目标字符串。如果我们使用一次性子组
和后瞻断言
,这个问题就很好解决。/(?>.*)(?<=test)/
,当(?>.*)
匹配了整个字符之后,后瞻断言(?<=test)
只需要判断字符串的后四个是不是"test"
就可以了,如果不是"test"
,那断言失败,则整个正则表达式就匹配失败,.*
部分不用再回溯。可以很好的提高匹配的性能。
所以说在程序中善于运用一次性子组和断言,可以很好的提升匹配的速度,从而提高程序的性能。
查看笔记