JavaScript 正则表达式(2)


JavaScript正则表达式(1)中,我们学习了如何声明一个正则对象以及正则里常用的一些元字符,正则对象的方法和字符串里的正则方法。下面,我们来一起学习入门拓展的JavaScript正则表达式


正则表达式的反向引用###

在正则表达式中,括号能有‘记住’他们包含的子表达式匹配的文本。所以,如果我们运用反向引用的方法去匹配类似于AA,叠字类型的文本,就简单很多了,当然,反向引用的作用不止如此。具体代码如下:

var reg =/(c.(t))(123)\1\2\3/ig;
var text = '------cat123catt123-------';
text.match(reg);//["cat123catt123"]

\1是匹配第一个括号里面的,\2是匹配第一个括号里面嵌套的括号,\3是匹配最后一个括号。


正则表达式的分组匹配###

分组匹配运用括号的原理和反向引用类似,只是反向引用作用在正则表达式‘内部’,分组匹配在正则表达式‘外部’,语言的形容是苍白无力的,请看下面的具体代码:

var reg =/(c.(t))(123)\1\2\3/ig;
var text = '------cat123catt123-------';
text.match(reg);
console.log(RegExp.$1);//cat
console.log(RegExp.$2);//t
console.log(RegExp.$3);//123

在上述,我们知道可以用()保留我们想要的数据,并通过一些方法,比如$1\1取得,如果你不想让()保留,可以使用(?:···)的方式,让这个括号变为非捕获性括号。这样,正则就不会记录里面的数据了。


正则表达式中的环视(lookaround)###

在了解环视之前,我们一起来思考一个问题,比如有这么一串数字28000000000,看起来是不是很累?所以一般情况下,我们需要给它加上,,就像这样28,000,000,000,但是问题来了,应该如何给这个字符串加上,呢,我们知道是从右往左,一次3个数字,加一个,,但是正则表达式都是从左往右解析的。聪明的你一定想到了,我们以左边至少有一个数字,右边是3的倍数个数字的规则去匹配,不就行了吗?所以,我们尝试写下了如下的正则表达式:

var reg = /([0-9]+)([0-9][0-9][0-9])+/g;
var text = '28000000000'
while(text!== text.replace(reg,'$1,$2')){
  text = text.replace(reg,'$1,$2')
};
console.log(text)//28,000,000,000

很显然,我们得到了我们想要的结果,但是是不是显得不太优雅?至少我认为是的,分析一下代码,我们发现,正则匹配是只要匹配过去之后就不会回头再去匹配(下一次匹配开始的地方是上一次匹配结束的地方),所以要通过一个while循环,一直遍历到无法匹配为止。
所以,在这个时候,环视的概念就显得尤为重要,它的概念是什么呢?
环视是不匹配任何字符,只匹配文本中的特定位置的一种结构。与^\b$有点类似。既然是一种结构,那么理所应当的,它在匹配的时候并不会占用字符
不占用字符是一种怎么样的概念呢?具体代码如下:

var reg = /moon(?=burn)/;
var reg2 = /moon(?:burn)/;
var text = 'moonburn';
var text1 = 'moonbUrn'
console.log(text.match(reg));//["moon", index: 0, input: "moonburn"]
console.log(text.match(reg2));//["moonburn", index: 0, input: "moonburn"]
console.log(text1.match(reg));//null

通过上述代码,应该很容易就可以发现,环视的作用,使用环视的方式就是(?=···),而moon(?=burn)的意思就是说,匹配一个moon然后后面是burn的字符串,moon(?:burn)的意思其实和moon(?=burn)理解起来是差不多的,但是还是有一个很大的区别,就是上面提出的,用环视的方式进行匹配,匹配的字符本身不会被算为占用的字符,也就是说,用moon(?=burn),匹配到n的时候,遇到了环视,然后判断后续是否符合环视的规则,如果符合,返回moon,如果不符合,返回null,而moon(?:burn)的意思是,匹配到n之后,继续向后匹配burn,如果都匹配成功,返回moonburn(因为burn也是匹配项),失败,返回null。这就是环视最大的优点匹配的时候并不会占用字符

环视的种类####

当然,为了需要,环视也不仅仅只有(?=···)一种,还有以下的:

类型 正则表达式 匹配成功的条件
肯定顺序环视 (?=···) 子表达式能够匹配右侧文本
否定顺序环视 (?!···) 子表达式不能匹配右侧文本

环视大家差不多已经有所了解了,那么回到一开始的那个例子,通过环视。如何更优雅的给一串数字加上,呢?具体代码如下:

var reg =/([0-9])(?=([0-9][0-9][0-9])+(?![0-9]))/g;
var text = '28000000000';
text.replace(reg,'$1,');//28,000,000,000

是不是通过环视就把while循环去掉了,变得清爽了很多?确实,环视的作用确实蛮大的,希望大家能够理解,熟练运用并掌握。


字符组简记法

在上述例子中,我们用[0-9]来匹配一个数字,其实在JavaScript中(很多其他语言也是一样的),用\d来表示匹配一个数字,所以,上面的代码可以改为:

var reg =/(\d)(?=(\d\d\d)+(?!\d))/g;
var text = '28000000000';
text.replace(reg,'$1,');//28,000,000,000

是不是更加清爽了?在我看来,[0-9]\d是完全等价的,只是\d的写法更简单,明了一些。这种就叫做字符组简记法,当然,不仅仅只有\d一个,更多的如下:

字符 匹配对象 注释
\d 数字 等价于[0-9]
\D 非数字字符 等价于[^\d]
\w 单词中的字符 等价于[a-zA-Z0-9_](至少在JavaScript是这样)
\W 非单词字符 等价于[^\w]
\s 空白字符 等价于[ \f\n\r\t\v]
\S 非空白字符 等价于[^\s]

JavaScript中正则的引擎

首先,要知道,正则的引擎种类繁多,但是大致可以分为2个大类,一种是NFA,一种是DFA。JavaScript用的是NFA引擎。通过简单的代码判断:

var reg = /nfa|nfa not/g;
var text = 'nfa not';
text.match(reg)//["nfa"];

可以得知,JavaScript是传统型的NFA,不是POSIX NFA

在了解不同引擎的差异之前,我们可以先来了解一下它们的共同点。以下共同点适用于所有引擎:

  1. 优先选择最左端(最开头)的位置匹配。
  1. 标准的匹配量词*,+,?{m.n}是匹配优先的。

下面 开始讨论第一条规则,优先选择最左端(最开头)的位置匹配
举个例子,例如以下情况:

var reg = /cat/g;
var text = '123catt45678cat'
text.search(reg)//3

text中,满足reg的位置有2个,分别是3,12,因为优先选择左端的位置,所以匹配了3位置的cat

再来一个例子:

var reg = /cat|dog|monkey/g;
var text = '123monkeydogcatt45678cat'
text.search(reg)//3

这个例子很简单,告诉我们,正则表达式的匹配是所有的匹配项都会尝试一遍,哪怕monkey项在正则的最后面,也会最先匹配。
下面开始讨论第二条规则,标准的匹配量词*,+,?{m.n}是匹配优先的
关于上述的匹配量词,都是匹配优先的,匹配优先是个什么意思呢?比如,当我们用了+来进行匹配的时候,当匹配到一个满足条件之后,他会继续往下匹配,一直匹配到无法匹配为止。如果我们看到匹配出来的结果不是最大匹配项,那么一定是因为最大匹配项的情况下无法满足后续的匹配规则,就减少匹配项从而满足后续项,这种全部匹配的过程,就是匹配优先的过程。举个例子:

var reg = /[0-9]+0/g;
var text = '12340567890123';
text.match(reg);//["12340567890"]

这个是匹配成功了,一开始的[0-9]+就把1234567890123全部匹配完了(匹配优先,就是这么厉害!不服不行!),但是匹配完了之后,发现无法满足后面的匹配项0,没有办法,只能退回一个,看看能不能满足,于是[0-9]+的匹配项变成了``123456789012,发现还是不行,重复上述过程······一直退回到123456789,后面匹配到0,满足了!搞定收工!返回了["12340567890"],细心的你,一定是发现了,其实在前面的12340也是满足这个正则的,并且是排在了更加靠左的地方,但是却被忽略了,为什么?因为有关匹配量词的引擎内部的计算方法就是这样的,或者说因为**匹配优先**,而选择了后面的符合条件的1234567890`。
为了巩固大家对匹配优先的理解,我准备趁热打铁,再来一个可能大家会混淆的例子(希望是我多虑了):

var reg = /[0-9]+([0-9]+)/g;
var text = '12340567890123';
text.match(reg);
RegExp.$1;//是多少呢?

先允许我卖个关子,先来分析一波,想一下匹配优先的原则,一开始的[0-9]+text全部匹配完了,然后慢慢退回,退回一个3([0-9]+)之后,满足了,完成走人......可能有人会想,后面一个也是+啊为什么才给它一个数字呢,没办法,先来先服务嘛,勉强给你一个满足就不错了,你还要几个?还要啥自行车?所以RegExp.$1的值是3

答对了吗?如果答对了,那你应该对匹配优先了解透彻了,如果没有答对或者很犹豫,那请你在看看前文吧。因为万丈高楼平地起,很多复杂的正则表达式都是从这个开始的。基础尤为重要。



JavaScript 正则表达式(1)
JavaScript 正则表达式(2)
JavaScript 正则表达式(3)
JavaScript 正则表达式(4)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容