正则表达式-学习笔记

原文:http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FVIM%2Fusage%2Fregexp%2FM.1180953641.00

常用的正则表达式引擎(NFA)大都提供了Greedyqualifier,Lazyqualifier(在Java中称为Reluctantqualifier)和Atomicgrouping(在Java中对应的概念是Possessivequalifier),但开发人员一般用到的只是最普通的,默认的qualifier,许多人甚至根本不了解qualifier的分类。那么,这三个概念的,到底有什么区别呢?


先来看Greedyqualifier和Lazyqualifier,要了解这两者的区别,必须引入一个概念,就是回溯(Backtracking)。

简单的说,回溯就是记录已匹配的子表达式的可能状态(例如,待匹配串中为字符aa,正则表达式为a+,则回溯机制会记录:a+既可以匹配a,也可以匹配aa,并选取其中之一作为当前尝试),如果接下来的子表达式无法匹配,就需要回溯,更改之前表达式的状态。

下面我们来看一个回溯的例子:

若待匹配串为

abcdefg6

正则表达式为

^.*\d$

正则引擎会从左至右开始匹配,因为.可以匹配任何字符,则.*会匹配整个字符串abcdefg6(实际上,每前进一个字符,正则引擎都保存了选择信息以供回溯),此时已经前进到待匹配串的末端,而正则式尚未匹配完成,引擎便会启用回溯机制——\d会对.*说,我来了,吃了我的,给我吐出来J——.*于是回退一位,把6交出来,\d匹配完成,最后,换行符与$匹配,则整个正则式的匹配完成。

了解了回溯,再来看Greedyqualifier和Lazyqualifier就很容易了。

Greedyqualifier之所以得名,是因为它记录下回溯信息之后,总是尝试“满足”(match)的选项。举例来说,\d?代表匹配一位或零位数字,Greedyqualifier记录下两个选项之后,首先尝试的便是匹配一位数字。

而Lazyqualifier在记录下回溯信息之后,首先尝试的是“不满足”(notmatch)的选项。仍然举\d?的例子(注意,这里只是为了利用\d?的概念,应用时需要另加修饰符规定\d?为一个Lazyqualifier),如果是Lazyqualifier,则首先尝试“不匹配数字”的分支。

一般来说,Greedyqualifier是默认的选项,我们通常使用的qualifier就是Greedyqualifier。如果需要采用Lazyqualifier,开发人员需要把\d?写作\d??(在Java语言中,Lazyqualifier的标志是在相应的Greedyqualifier之后加上一个?),正则引擎才能正确识别。

至此,我们可以看一个例子:

Thename“McDonald’s”issaid“makudonarudo”inJapenese

如果使用”.*”匹配,则取得“McDonald’s”issaid“makudonarudo”

如果使用”.*?”匹配,则取得“McDonald’s”


熟练的使用这两种qualifier,能为开发人员提供极大的便利。

然而,仅有这两种qualifier还是不够的,因为回溯需要记录之前的选择分支,很多时候会影响匹配的效率,来看下面的例子:

正则表达式为

^\w+:

待匹配串为

ThisIsAnInefficientMatch

希望匹配的是以新行为开头,以:结尾的一串字符,我们一眼就能看出,待匹配串不包含这种子串,但正则引擎需要进行多次回溯(在匹配:之前,\w+已经匹配了整行,每个字符之前都保留了一个选择分支)才能得出结果,即便换用^\w+?:,仍然需要进行多次无谓的尝试。



正因为如此,正则引擎提供了AtomicGrouping的机制。

简单的说,AtomicGrouping的主要功能便是取消回溯,提高效率——如果匹配成功,它与普通的grouping并无区别,但是如果匹配失败,所有位于AtomicGrouping中的状态会全部失效。

来看上一个例子,如果我们为\w+子串加上AtomicGrouping,写作(?>\w+),整个正则表达式变为^(?>\w+):,在group内部,\w+是一个greedyqualifier,将会匹配整行字符,并且记录下多个回溯状态(注意,这里的记录都在AtomicGrouping内部),然后开始匹配:,正则引擎发现:无法匹配,此时无需回溯(?>\w+)内部的分支,而是将它们全盘放弃,这样极大地提高了匹配(主要是匹配失败)的效率。


一些语言或许没有提供AtomicGrouping的功能,或者以另一种方式提供,例如Java语言引入了Possessivequalifier,其作用与AtomicGrouping类似,只是写法不同。上面的例子中,AtomicGrouping的(?>\+),用Possessivequalifier,写作\w++。


你可能感兴趣的:(正则表达式)