猫窝私语 — Makumo's Blog

玛酷猫的温馨小窝,记录生活点点滴滴。

@玛酷猫13 年前

04/6
11:52
JS&JQUERY

正则表达式中的断言和非捕捉组

前段时间在cnblogs看到一道有意思的题目,传送门地址。题面很简单:将一段数字从右到左每隔三位插入一个逗号 ,也就是通常说的插入千分位符,使用javascript实现,据说是某大公司的面试题。动态语言就没有什么难点了,现成的函数都有。我第一反应也就是把类似字符串倒转原理的操作,只不过不是一位一位,是三位三位,然后用分号连接起来。各式各样的解答和效率测试就不多说了,源地址的博客中都有说明。单单说下其中的一个解答,如下:

var cuter2 = function( str ){//abcd
     return str.replace( /\B(?=(?:\d{3})+$)/g, ',' );
};

看到这个解答很神奇,单单一句正则就搞定,虽然效率一般,在页面大量需要此操作的情况下并不是很适用。老实说正则也写过不少,以前做页面抓取,各式各样的匹配也写过,但是这个正则表达式基本理解不能。幸好手上有正则利器,一个叫“Regex Match Tracer”的小工具(我就不贴下载地址,可自行搜索),可以分解测试正则表达式。去掉/g全局参数和开头的/,正则的结构分解的很清楚,见右图。其中有两个词倒是第一次见,一个是零宽度断言,另一个就是非捕捉组。网上搜索一下,明了很多。

先说非捕捉组,这个很好理解,用法(?:Expression)。非捕捉组主要是整体效率的考虑,因为并不是所有的捕捉组都是我们需要的,有一些为了方便分割,有一些是必须使用捕捉组的形式,往往并不关心捕捉组的具体内容,每一个捕捉组都会消耗一部分内存,当一个很长或者重复很多的时候,这些捕捉组消耗的内存就不是个小数目了。

再说说断言,这个比较难理解,先引用网上搜索到的说明:

元字符/b、^、$都匹配一个位置,且这个位置满足一定的条件。在此,把满足这一个条件称为断言或零宽度断言。

表达式(?=experssion)、(?!experssion)、(?<=experssion)和(?<!experssion)都是匹配一个位置。
(?=experssion)又称为零宽度正预测先行断言,它断言自身位置的后面能够匹配表达式experssion。
(?<=experssion)又称为零宽度正回顾后发断言,它断言自身位置的前面能够匹配表达式experssion。
(?!experssion)又称为负向零宽度断言或者零宽度负预测先行断言,它断言自身位置的后面不能匹配字符串experssion。
(?<!experssion)又称为零宽度负回顾后发断言,它断言自身位置的前面不能匹配字符串experssion。

说明比较绕口,个人理解为一般的正则是匹配字符或者字符串,而断言是匹配符合一定条件的位置,就拿上面那个正则说明(个人理解):\d{3}是三位数字组,(?:\d{3})非捕捉组,(?:\d{3})+贪婪模式,一个或者多个三位数字组合,(?=(?:\d{3})+$)断言,以一个或者多个3位数字组合结尾的那个位置,最后前面的/B是匹配不是在字符的开始或结束位置,防止字符串正好是3的倍数,千分号打在最开头。/g全局模式,因为一次只能将一个位置替换成逗号。其实弄清楚了每一小部分,整个正则还是很好理解的。主要就是能不能想到这个方法。

最后再多说一些,随着javascript的框架应用层出不穷,很多做前端的为了开发效率考虑,直接就使用框架来开发,在这个速成的时代,甚至有些前端只会使用框架写法,基本的javascript都写不出几行出来。这是一个很恐怖的事情,就好比一个学步的人,天天使用代步工具,汽车、飞机、甚至电动轮椅,一旦脱离这些东西,这个人将寸步难行。框架也是同样的原理,不管什么语言的框架,都是为了更高的开发效率,但是框架的实现原理也需要研究弄懂的,大部分框架都是开源的,花些时间研究并不是很困难的事情,一方面可以巩固自身的语言基础用法,最重要的是从架构上理解开发者为什么要这样部署框架结构,框架运行思想,也能从中发现存在的瑕疵或者不适应自身项目的地方,加以完善或者改进调整,同时也能为自身在系统架构认识上面,增加不少的经验。

正则表达式中的断言和非捕捉组