2011-10-25

有关正则表达式中非捕获组的网上总结和个人见解[zz]

正则表达式是个极其麻烦的东西,这篇博文对非捕获组的讲解比较详细。

(另捕获组讲解http://blog.csdn.net/lxcnn/article/details/4146148

转自http://blog.sina.com.cn/s/blog_5a6f39cf0100n9ki.html

看到7.3.4 非捕获性分组这里,书上说非捕获性分组不会创建反向引用,下面是例子:
var sToMatch = “#123456789″;
var reNumbers = /#(?:\d+)/;
reNumbers.test(sToMatch);
alert(RegExp.$1);
这里alert输出是空的。
var sToMatch = “en-us rv:0.9.4″;
var reNumbers = /rv:(\d+\.\d+(?:\.\d+)?)/;
var result = reNumbers.test(sToMatch);
alert(RegExp.$1);
这里alert输出就成了0.9.4。
最后那个.4是非捕获性分组捕获到的。这里就奇怪了,为什么前面的非捕获性分组不能捕获,而嵌套的非捕获性分组就又可以捕获了?而且这里好像不用非捕获性分组照样可以达到相同的目的,为什么要用非捕获性分组呢?

解答一:
因为RegExp.$1取的是第一个捕获组而且.4包含在第一个捕获组中,所以取RegExp.$1时可以取到。而.4所在的第二个组是非捕获组,当取RegExp.$2的时候.4是取不到的。
如果改成:var reNumbers = /rv:(\d+\.\d+(\.\d+)?)/;则RegExp.$2是.4。上面问题中的(?:\.\d+)是非捕获组,内存不会保存你取到的值。所以RegexExp的组中应该就没有$2这一个值。RegExp.$2取到的是空的。
反向引用的时候(\d+\.\d+(?:\.\d+)?)–\1 这里是反向引用第一个捕获组,匹配–前后都一样的字符串。而(\d+\.\d+(?:\.\d+)?)–\2 是有错误的,因为第二个组是非捕获的,内存上就没有第二个组的内容,所以引用失败。
解答二:
要了解非捕获组就要先了解捕获组,之后再了解为什么会有非捕获组的出现。简单点说,捕获组就是把()中匹配到的内容保存到一个按“(”出现的顺序编号的组里,以供后续引用,引用的方式有反向引用,或是RegExp.$number等方式,不同的语言,支持的引用方式不同。
只要使用了(),默认为使用了捕获组,而这就带来一个问题,有些场景不得不使用(),但又不关心它匹配到的内容,比如写一个匹配24小时制HH:mm:ss的时间的正则如下:([01][0-9]|2[0-3])(:([0-5][0-9])){2},通常关心的只是整体的时间,并不关心局部的内容,这样就产生了一种副作用,将不关心的内容单独保存到内存中,只会浪费资源,降低效率。非捕获组就是为了抵消这一副作用来产生的,非捕获组只参与匹配,但不会把匹配到的内容捕获到组里。所以非捕获组根本就不参与编号,也就无从谈起它对应哪个$number。在取不存在的编号的捕获组时,有些语言会返回空字符串,有些语言会报异常。
(\d+\.\d+(?:\.\d+)?)中,整体是一个捕获组,按“(”出现的顺序,编号为1,(?:\.\d+)虽然是非捕获组,也是要参与匹配的,只是不将匹配结果单独保存到组里而已。
还需要说明的是,在绝大多数语言中,正则表达式整体对应的是$0,捕获组的编号是从1开始的。

本人见解:
本人在网上看到这个问题以及相关解答后,把所有的答案总结了一下,分成这两类。本人倾向于解答二,认为非捕获组不参与编号,没有对应的$number,也就肯定取不到值了。在取不存在的编号的捕获组时,有些语言会返回空字符串,有些语言会报异常。
本人为此试验了一下,在flashdevelop下编译代码:
var re:RegExp = /(i|you|he|she|it|we|they) likes? to (\w+)/i; //标示为句M
var match:Object = re.exec(“We like to party.”);
trace(match); //标示为句N
结果返回:We like to party,We,party(从显示结果可以看出match中含有三个元素,用’,'隔开)

然后把句M改成:var re:RegExp = /(i|you|he|she|it|we|they) likes? to (?:\w+)/i;
结果返回:We like to party,We(此时match中只含有两个元素了)
然后把句N改成:trace(match[2]);
结果返回:undefined(说明match中第三个元素没有定义)

然后把句M改成:var re:RegExp = /(?:i|you|he|she|it|we|they) likes? to (?:\w+)/i;
结果返回:We like to party(此时match中只含有一个元素了)
然后把句N改成:trace(match[1]);
结果返回:undefined(说明match中第二个元素没有定义)

从上述实验结果来看,非捕获组确实不参与编号,不在内存保存。并且在flashdevelop中如果非法访问不存在的元素,不会报异常,但是会显示undefined(这只是flashdevelop的效果,其他软件是报错还是怎么我就不知道了。)

说点啥吧