原文:http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FVIM%2Fusage%2Fregexp%2FM.1180953641.00
常用的正则表达式引擎(NFA)大都提供了Greedy qualifier, Lazy qualifier(在Java中称为Reluctantqualifier)和Atomic grouping(在Java中对应的概念是Possessive qualifier),但开发人员一般用到的只是最普通的,默认的qualifier,许多人甚至根本不了解qualifier的分类。那么,这三个概念的,到底有什么区别呢?
先来看Greedy qualifier和Lazy qualifier,要了解这两者的区别,必须引入一个概念,就是回溯(Backtracking)。
简单的说,回溯就是记录已匹配的子表达式的可能状态(例如,待匹配串中为字符aa,正则表达式为a+,则回溯机制会记录:a+既可以匹配a,也可以匹配aa,并选取其中之一作为当前尝试),如果接下来的子表达式无法匹配,就需要回溯,更改之前表达式的状态。
下面我们来看一个回溯的例子:
若待匹配串为
abcdefg6
正则表达式为
^.*\d$
正则引擎会从左至右开始匹配,因为.可以匹配任何字符,则.*会匹配整个字符串abcdefg6(实际上,每前进一个字符,正则引擎都保存了选择信息以供回溯),此时已经前进到待匹配串的末端,而正则式尚未匹配完成,引擎便会启用回溯机制——\d会对.*说,我来了,吃了我的,给我吐出来J——.*于是回退一位,把6交出来,\d匹配完成,最后,换行符与$匹配,则整个正则式的匹配完成。
了解了回溯,再来看Greedy qualifier和Lazy qualifier就很容易了。
Greedy qualifier之所以得名,是因为它记录下回溯信息之后,总是尝试“满足”(match)的选项。举例来说,\d?代表匹配一位或零位数字,Greedy qualifier记录下两个选项之后,首先尝试的便是匹配一位数字。
而Lazy qualifier在记录下回溯信息之后,首先尝试的是“不满足”(not match)的选项。仍然举\d?的例子(注意,这里只是为了利用\d?的概念,应用时需要另加修饰符规定\d?为一个Lazy qualifier),如果是Lazy qualifier,则首先尝试“不匹配数字”的分支。
一般来说,Greedyqualifier是默认的选项,我们通常使用的qualifier就是Greedy qualifier。如果需要采用Lazy qualifier,开发人员需要把\d?写作\d??(在Java语言中,Lazy qualifier的标志是在相应的Greedy qualifier之后加上一个?),正则引擎才能正确识别。
至此,我们可以看一个例子:
The name “McDonald’s” is said“makudonarudo” in Japenese
如果使用”.*”匹配,则取得“McDonald’s” is said“makudonarudo”
如果使用”.*?”匹配,则取得“McDonald’s”
熟练的使用这两种qualifier,能为开发人员提供极大的便利。
然而,仅有这两种qualifier还是不够的,因为回溯需要记录之前的选择分支,很多时候会影响匹配的效率,来看下面的例子:
正则表达式为
^\w+:
待匹配串为
ThisIsAnInefficientMatch
希望匹配的是以新行为开头,以:结尾的一串字符,我们一眼就能看出,待匹配串不包含这种子串,但正则引擎需要进行多次回溯(在匹配:之前,\w+已经匹配了整行,每个字符之前都保留了一个选择分支)才能得出结果,即便换用^\w+?:,仍然需要进行多次无谓的尝试。
正因为如此,正则引擎提供了AtomicGrouping的机制。
简单的说,Atomic Grouping的主要功能便是取消回溯,提高效率——如果匹配成功,它与普通的grouping并无区别,但是如果匹配失败,所有位于Atomic Grouping中的状态会全部失效。
来看上一个例子,如果我们为\w+子串加上Atomic Grouping,写作(?>\w+),整个正则表达式变为^(?>\w+):,在group内部,\w+是一个greedy qualifier,将会匹配整行字符,并且记录下多个回溯状态(注意,这里的记录都在AtomicGrouping内部),然后开始匹配:,正则引擎发现:无法匹配,此时无需回溯(?>\w+)内部的分支,而是将它们全盘放弃,这样极大地提高了匹配(主要是匹配失败)的效率。
一些语言或许没有提供Atomic Grouping的功能,或者以另一种方式提供,例如Java语言引入了Possessive qualifier,其作用与Atomic Grouping类似,只是写法不同。上面的例子中,Atomic Grouping的(?>\+),用Possessive qualifier,写作\w++。