javascript正则中的断言:正向预查(?=y),负向预查(?!y)
2017-04-25 00:39

js正则中的断言

js正则表达式中的断言有两种,一是正向预查(?=y),表示当它后面跟的是y的时候才匹配。另一个是负向预查(?!y),表示当它的后面跟的不是y的时候才匹配。

凡是用断言的地方,都可以采用正则的其他方式实现,只是比预查要麻烦。

下面,我们来看一段使用断言的正则。

var reg=/\d+(\.\d+)?(?=元每斤)/g;
var str="洋白菜的价格是36.5元每斤,现在太贵了,涨了13元";
var rst;
while((rst=reg.exec(str))!=null){
    console.log(rst);
}

这里我们使用了正向预查,当数字后面是"元每斤"的时候才匹配这个数字。

匹配的结果如下:

blob.png

注意一点,正向预查的意思是这个正则匹配的后面是y的时候才匹配,所以正向预查只有作为正则的末尾时候才能正确匹配我们想要的结果。

另外,这里使用exec进行匹配,正则表达式的模式一定要是全局的,要加“g”,不然这个while将会是一个死循环,因为如果设置了全局标志“g”,那么正则表达式exec方法将会从“lastindex”所代表的位置进行查找,否则的话,exec会忽略lastindex属性,从字符串的开始进行查找。而lastindex是表示匹配出结果后的下一个字符所在的位置。所以,设置g的话,第一次匹配到了"36.5",那么下次就是从"36.6元每斤"的“元”开始匹配,匹配到结尾没有匹配成功,返回null。但是,我们没有设置“g”的话,那么exec方法每次都会从字符串的开始进行匹配,就会每次都匹配到“36.5”,那么while循环就会一直执行下去,造成死循环。

负向预查的话,跟正向预查一样,也是必须要放在正则的末尾,才能按照我们所预期的结果执行。

var reg=/\d+(\.\d+)?(?!元|块)/g;
var str="我今天25岁了,现在的小龙虾又涨了3元,从25元每斤涨到了35块";
var rst;
while((rst=reg.exec(str))!=null){
    console.log(rst);
}

根据你的经验,你看到这段代码,心里预估的匹配结果应该是“25”。

可事实却不是这样,事实的匹配结果,是匹配到了三个结果,而不是一个“25”:

blob.png

25好理解,那就是25岁的25,而2是25元的2,3是35块的3。这就是负向预查的一个特点,它在匹配一个字符的时候,或先去看后面是不是“元|块”,如果后面不是元或快,再匹配"\d+(\.\d+)?"。那么第一次匹配,匹配到“25岁”的“25”的时候,后面是“岁”,匹配成功,第二次匹配,匹配到“3”的时候,后面是“元”,不符合,继续匹配,第三次匹配,匹配到“25元”的“25”时,后面是“元”,不匹配,但25是两个数字,往前退一个字符,匹配“2”,后面是“5”,匹配成功。

“35块”的“3”跟第三次匹配一样匹配到“3”。这个不见得是负向预查的真实执行过程,但确实一种理解方式。如果你有更好的理解方式,也是可以的。

也就是说负向预查是先按贪婪匹配进行匹配,匹配不成功的话,再退回按照非贪婪匹配进行匹配。而上面的正向预查不存在这种问题,它是先预查后面的是y的时候才进行贪婪匹配。跟我们正常的理解比较符合。

在负向预查中,我们使用了“|”或者符号,正向预查中也是可以使用"|"符号的。

但是在负向预查中的“|”和正向预查中的"|"的意思确实相反的,正向预查中的“(?=a|b)”,表示后面是a后者b时都会匹配成功;负向预查中的“(?!a|b)”,表示后面不能是a,并且不能是b。

解释了他们的特征,我们再来把我们上面的负向预查修改成只能匹配出”25“的正则。

var reg=/\d+(\.\d+)?(?!\d|元|块)/g;
var str="我今天25岁了,现在的小龙虾又涨了3元,从25元每斤涨到了35块";
var rst;
while((rst=reg.exec(str))!=null){
    console.log(rst);
}

我们在负向预查中添加了一个"\d",那就是表示数字后面不能再是数字了,进而也就排除了剩下两个单个数字的匹配了。

blob.png

而这个匹配结果也进步一作证了上面我对负向预查执行过程的猜想,是先按照贪婪匹配进行匹配,后按照非贪婪匹配进行匹配的。

本文讲解的断言中,正向预查比较好理解,而负向预查的注意点比较多,而且理解起来也比正向预查要麻烦点。对于负向预查的执行过程的猜想就是为了帮助大家理解负向预查的特性以及它的执行结果的。

最后再重申一句,断言中正向预查和负向预查只能用在正则表达式的末尾,不能用在正则表达式的开始或者中间。

下面这种写法是错误的,是匹配不出任何结果的:

var reg=/(?=今天)\d+(\.\d+)?/g;
var str="我今天25岁了,现在的小龙虾又涨了3元,从25元每斤涨到了35块";
var rst;
while((rst=reg.exec(str))!=null){
    console.log(rst);
}

这是错误的,错误的,错误的,切勿这样使用js正则的断言。

原创文章,转载请注明来自:妹纸前端-www.webfront-js.com.
阅读(3777)
辛苦了,打赏喝个咖啡
微信
支付宝
妹纸前端
妹纸前端工作室 | 文章不断更新中
京ICP备16005385号-1