Skip to content

正则表达式possessive、greediness和laziness区别

pro648 edited this page Jun 2, 2017 · 1 revision

正则表达式(Regular Expression)的贪婪模式(Greediness)和懒惰模式(Laziness)用于决定匹配的排列顺序。

贪婪模式首先尝试尽可能多的匹配,最终正则表达式引擎回溯找到匹配。懒惰模式首先尝试尽可能少的匹配,最终正则表达式引擎逐行扩展匹配,以找到整体匹配。因为贪婪模式和懒惰模式改变了排列顺序,它们可以改变整体正则表达式匹配。但是,它们不会改变正则表达式引擎为防止遗漏可能的匹配项,将进行回溯以便尝试所有排列可能。

Possessive Match会阻止正则表达式引擎尝试所有排列可能性,主要好处在于提高性能。

Possessive Match工作原理

与贪婪模式类似,Possessive Match也是尽可能多的匹配,不同之处在于它不会放弃匹配作为引擎回溯。Possessive Match通过在运算符后面添加+生成。*是贪婪匹配,*?是懒惰匹配,*+是possessive匹配,++?+{n,m}+都是possessive匹配。

iOS正则表达式语法全集内可以查看懒惰匹配、贪婪匹配及possessive匹配都有哪些。

来看一下模式"[^"]*+"匹配字符串"abc"的过程,"匹配左双引号“,[^"]匹配字符a、b和c,因为*表示零次、一次或多次,"匹配最后的字符“,到此,找到了完整匹配。在这里使用贪婪模式或possessive match匹配结果没有区别,Possessive match因为不需要记录回溯位置,会更高效些。

在遇到正则表达式匹配失败时,Possessive Match对于性能的提高更为明显。如果上面的字符串为“abc,没有右双引号,这里的匹配过程与上面类似,不过最后的"会匹配失败。当使用Possessive Match时,失败后不进行回溯,即不会放弃已匹配的部分以便尝试其它排列,所以在右双引号"匹配失败时,整个匹配尝试立即停止。

如果用的模式为"[^"]*",正则表达式引擎会进行回溯。当最后的"匹配失败时,[^"]*进行回溯,放弃最后一个匹配,即匹配字符改变为ab,"尝试匹配字符c,失败。[^"]*进行回溯,放弃一个匹配,即匹配字符改变为a,"尝试匹配字符b,失败。[^"]*再次回溯,放弃一个匹配,即不匹配任何字符,"尝试匹配字符a,失败。只有到这时,所有回溯位置用完,正则表达式引擎才会放弃匹配。最终,正则表达式执行了很多无用匹配尝试。

Possessive Match最大的好处是可以加快正则表达式的匹配速度,可以让正则表达式更快的失败。在上面的例子中,我们知道regex不可能错过右双引号,所以在匹配失败时没有必要进行回溯,可以通过使用Possessive Match避免回溯。

只有一个回溯时可能无法觉察到效率的提升,当这些运算符嵌套在一个组内,且都是重复*,并且该组也重复时,此时的回溯会是灾难性的,这时使用Possessive Match会对性能有明显提高。

Possessive Match注意事项

使用Possessive Match可以改变匹配的结果。如模式".*"匹配字符串"abc"x的结果为“abc”,模式".*+"不能匹配该字符串。

在上面的两个regex中,第一个"匹配左双引号“,Possessive Match的.匹配剩余字符串abc"x,右双引号"匹配失败,因为不能回溯,没有其它排列可能,整个匹配失败。贪婪匹配开始时匹配剩余字符串abc“x,右双引号"匹配失败。开始回溯,一次回溯一个匹配,在这里为一个字母或符号。回溯匹配为abc","匹配字符x失败。回溯匹配为abc,"匹配右双引号“,匹配完成,匹配项为"abc"。

当使用Possessive Match时,要注意Possessive匹配符+前后的字符不能匹配。正如上面的例子,possessive match前的.能够匹配possessive match后的右双引号,这时就不能使用possessive match。

如果想要系统了解正则表达式,请查看我的另一篇文章正则表达式NSRegularExpression

参考资料:

  1. Possessive Quantifiers

欢迎更多指正:https://github.com/pro648/tips/wiki

Clone this wiki locally