006.聊聊 JavaScript 正则表达式 下卷

有点复杂,是转载的文章

贪婪模式与非贪婪模式讲解

Javascript中的正则贪婪与非贪婪模式的区别是:

  • 被量词修饰的子表达式的匹配行为;
  • 贪婪模式在整个表达式匹配成功的情况下尽可能多的匹配;
  • 非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配;

一些常见的修饰贪婪模式的量词如下:

贪婪和非贪婪

1. 什么是贪婪模式?

var str = "longen<p>我是中国人</p>yunxi<p>我是男人</p>boyboy";
// 贪婪模式 匹配所有字符
console.log(str.match(/<p>.*<\/p>/)[0]); 
// <p>我是中国人</p>yunxi<p>我是男人</p>

// 后面加问号,变成非贪婪模式
console.log(str.match(/<p>.*?<\/p>/)[0]); // <p>我是中国人</p>
我们来理解下匹配的基本原理;
首先正则是 /<p>.*</p>/ 匹配;<p>匹配字符串第一个字符匹配失败,接着往下,直接匹配到<p>时候,匹配成功,接着把匹配的控制权交给.*,从匹配到的<p>位置开始匹配,一直到</p>之前,接着把控制权交给</p>,接着在当前位置下往下匹配,因此匹配到</p>,匹配成功;由于它是贪婪模式,在匹配成功的前提下,仍然会尝试向右往下匹配,因此会匹配到两个<p>标签结束;但是非贪婪模式,也就是第二个console.log();他匹配到第一个p标签成功后,它就不会再进行匹配,因此打印的值为一个p标签的内容了;

理解匹配成功前提下的含义: 上面我们解释了,贪婪模式是要等一个表达式匹配成功后,再往下匹配;比如我们现在接着再看下们的表达式匹配代码:

var str = "longen<p>我是中国人</p>yunxi<p>我是男人</p>boyboy";
// 贪婪模式
console.log(str.match(/<p>.*<\/p>yunxi/)[0]); 
//打印 <p>我是中国人</p>yunxi 

如上代码,打印出一个p标签内容,我们知道.*是贪婪模式匹配,如果按照上面的解释会打印出2个p标签的内容,因为这里在匹配到p标签后,会继续匹配后面的yunxi字符串,直到整个表达式匹配成功,才提示匹配成功,因此当匹配到第一个yunxi后,第一个子表达式匹配成功,接着尝试往右继续匹配,<p></p>都匹配成功,但是yunxi不会匹配boyboy,所以第二次尝试匹配的子表达式匹配失败,因此只返回匹配成功后的第一个p标签的内容了;

我们现在再来看看非贪婪模式的含义:
测试代码:

var str = "longen<p>我是中国人</p>yunxi<p>我是男人</p>boyboy<p>我是中国人2</p>yunxi<p>我是男人</p>boyboy";
// 非贪婪模式1
console.log(str.match(/<p>.*?<\/p>boyboy/)[0]); 
//<p>我是中国人</p>yunxi<p>我是男人</p>boyboy

// 贪婪模式
console.log(str.match(/<p>.*<\/p>yunxi/)[0]); 
//<p>我是中国人</p>yunxi<p>我是男人</p>boyboy<p>我是中国人2</p>yunxi

我们先看表达式1,/<p>.*?</p>boyboy/ 匹配str字符串,首先先匹配到p标签内容;但是由于boyboy字符串一直没有匹配到,因此会一直尝试往后匹配,直到匹配到boyboy字符串后,才匹配成功,否则匹配失败;由于它是非贪婪模式,因此这时候它不会再往下进行匹配,所以匹配就结束了;因此第一个console输出为<p>我是中国人</p>yunxi<p>我是男人</p>boyboy;

我们可以再来看看贪婪模式 第二个console.log()输出的; 正则表达式 /<p>.*</p>yunxi/ 匹配到第一个p标签yunxi后,由于它是贪婪的,它还想接着向右继续匹配,直到匹配完成后,匹配成功,才结束,因此把所有p标签后面跟随yunxi的字符串都匹配到,且之间的所有的字符串都被返回;

理解正则表达式匹配原理

我们先来理解下占有字符和零宽度的含义。

1. 占有字符和零宽度

在正则表达式匹配的过程中,如果子表达式匹配到的是字符内容,而非位置的话,并被保存在匹配的结果当中,那么就认为该子表达式是占有字符的;如果子表达式匹配的仅仅是位置,或者说匹配中的内容不保存到匹配的结果当中,那么就认为该子表达式是零宽度的。我们先来理解下零宽度的列子,最常见的就是环视~ 它只匹配位置;比如顺序环视;环视待会就讲;

说明

正则匹配方式 /abc/
匹配过程:首先由字符a取得控制权,从位置0开始进行匹配,a匹配a,匹配成功;接着往下匹配,把控制权交给b,那么现在从位置1开始,往下匹配,匹配到字符串b,匹配成功,接着继续往下匹配,位置是从2开始,把控制权交给c,继续往下匹配,匹配到字符串c,匹配成功,所以整个表达式匹配成功;匹配结果为 abc 匹配的开始位置为0,结束位置为3;

含有匹配优先量词的匹配过程

image.png

源字符串abc,正则表达式为ab?c ;量词?可以理解为匹配优先量词,在可匹配可不匹配的时候,会优先选择匹配;当匹配不到的时候,再进行不匹配。先匹配b是否存在,如果不存在的话,就不匹配b;因此结果可以匹配的有 abc,ac等

匹配过程:

首先由字符a取得控制权,从位置0开始匹配,a匹配到字符串a,匹配成功;接着继续匹配,把控制权交给b,b现在就从位置1开始匹配;匹配到字符串b,匹配成功;接着就把控制权交给c,c从位置2开始继续匹配,匹配字符串c,匹配成功;整个表达式匹配成功;假如b那会儿匹配不成功的话,它会忽略b,继续匹配字符串c,也就是如果匹配成功的话,结果是ac;
因此abc匹配字符串abc,匹配的位置从0开始,到3结束。
如果匹配的结果为ac的话,那么匹配的位置从0开始,到2结束;
假如我们把字符串改为abd,或者abe等其他的,那么当匹配到最后一个字符的时候,就匹配失败;

含有忽略优先量词的匹配过程

image.png

量词?? 含义是 忽略优先量词,在可匹配和可不匹配的时候,会选择不匹配,这里的量词是修饰b字符的,所以b?? 是一个整体的。匹配过程如下

首先由字符a取得控制权,从位置0开始匹配,有”a”匹配a,匹配成功,控制权交给b?? ;首先先不匹配b,控制权交给c,由c来匹配b,匹配失败,此时会进行回溯,由b??来进行匹配b,匹配成功,然后会再把控制权交给c,c匹配c,匹配成功,因此整个表达式都匹配成功;

理解正则表达式----环视

环视只进行子表达式匹配,不占有字符,匹配到的内容不保存到最终的匹配的结果,是零宽度的,它匹配的结果就是一个位置;环视的作用相当于对所在的位置加了一个附加条件,只有满足了这个条件,环视子表达式才能匹配成功。环视有顺序和逆序2种,顺序和逆序又分为肯定和否定,因此共加起来有四种;但是javascript中只支持顺序环视,因此我们这边来介绍顺序环视的匹配过程;

如下说明:
1.(?=Expression): 顺序肯定环视,含义是所在的位置右侧位置能够匹配到regexp.
2.(?!Expression): 顺序否定环视,含义是所在的位置右侧位置不能匹配到regexp

顺序肯定环视

image.png

首先我们需要明白的是:^和$ 是匹配的开始和结束位置的;?= 是顺序肯定环视,它只匹配位置,不会占有字符,因此它是零宽度的。这个正则的含义是:
以字母或者数字组成的,并且第一个字符必须为小写字母开头;
匹配过程如下:

首先由元字符^取得控制权,需要以字母开头,接着控制权就交给 顺序肯定环视 (?=[a-z]); 它的含义是:要求它所在的位置的右侧是有a-z小写字母开头的才匹配成功,字符a12,第一个字符是a,因此匹配成功;我们都知道环视都是匹配的是一个位置,不占有字符的,是零宽度的,因此位置是0,把控制权交给[a-z0-9]+,它才是真正匹配字符的,因此正则[a-z0-9]+从位置0开始匹配字符串a12,且必须以小写字母开头,第一个字母是a匹配成功,接着继续从1位置匹配,是数字1,也满足,继续,数字2也满足,因此整个表达式匹配成功;最后一个$符合的含义是以字母或者数字结尾的;

顺序否定环视
当顺序肯定环视匹配成功的话,顺序否定环视就匹配失败,当顺序肯定环视匹配失败的话,那么顺序否定环视就匹配成功;

image.png

源字符串:aa<p>one</p>bb<div>two</div>cc
正则:<(?!/?p\b)[^>]+>
正则的含义是:匹配除<p>之外的其余标签;
如下图:


image.png

匹配过程如下:

首先由”<” 取得控制权,从位置0开始匹配,第一个位置和第二个位置都是字符a,因此匹配失败~ 接着从位置2匹配,匹配到<, 匹配成功了,现在控制权就交给(?!/?p\b);?!是顺序否定环视,只匹配一个位置,不匹配字符,这个先不用管,首先是 /? 取得控制权,它的含义是:可匹配/,或者不匹配/, 接着往下匹配的是p字符,匹配失败,进行回溯,不匹配,那么控制权就到一位了p字符,p匹配p,匹配成功,控制权就交给\b; \b的含义是匹配单词的边界符,\b就匹配到了 > ,结果就是匹配成功,子表达式匹配就完成了;/?p\b 就匹配成功了;所以(?!/?p\b) 这个就匹配失败了;从而使表达式匹配失败;我们继续往下匹配,从b字符开始,和上面一样匹配失败,当位置是从14开始的时候 < 字符匹配到”<”,匹配成功,把控制权又交给了(?!/?p\b), 还是/?取得控制权,和上面匹配的逻辑一样,最后?p\b匹配失败了,但是(?!/?p\b) 就匹配成功了,因此这一次表达式匹配成功;如下代码匹配:
var str = "aa<p>one</p>bb<div>two</div>cc";
// 匹配的结果为div,位置从14开始 19结束
console.log(str.match(/<(?!/?p\b)[^>]+>/)[0]);

理解正则表达式---捕获组

捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显示命名的组里,方便后面使用;可以在正则表达式内部使用,也可以在外部使用;
捕获组有2种,一种是捕获性分组,另一种是 非捕获性分组;
我们先来看看捕获性的分组语法如下:
捕获组分组语法:(Expression)
我们都知道中括号是表示范围内选择,大括号表示重复次数,小括号的含义是允许重复多个字符;
捕获性分组的编号规则:编号是按照”(”出现的顺序,从左到右,从1开始进行编号;
比如如下代码:

// 分组的列子
console.log(/(longen){2}/.test("longen")); // false
console.log(/(longen){2}/.test("longenlongen")); //true
// 分组的运用 RegExp.$1 获取小括号的分组
var str = 11122;
/(\d+)/.test(str);
console.log(RegExp.$1); // 11122

// 使用replace替换 使用分组 把内容替换
var num = "11 22";
var n = num.replace(/(\d+)\s*(\d+)/,"$2 $1");
console.log(n); // 22 11

反向引用

反向引用标识由正则表达式中的匹配组捕获的子字符串。每个反向引用都由一个编号或名称来标识;并通过 ”\编号” 表示法来进行引用;

// 反向引用
console.log(/(longen)\1/.test("longen")); // false
console.log(/(longen)\1/.test("longenlongen")); // true

理解非捕获性分组

并不是所有分组都能创建反向引用,有一种分组叫做非捕获性分组,它不能创建反向引用,要创建一个非捕获性分组,只要在分组的左括号的后面紧跟一个问号与冒号就ok;非捕获分组的含义我们可以理解为如下:子表达式可以作为被整体修饰但是子表达式匹配的结果不会被存储;如下:

// 非捕获性分组
var num2 = "11 22";
/#(?:\d+)/.test(num2);
console.log(RegExp.$1); //""
我们再来看下使用 非捕获性分组来把页面上的所有标签都去掉,如下代码:
// 把页面上所有的标签都移除掉
var html = "<p><a href='http://baidu.com'>我来测试下</a>by <em>龙恩</em></p>";
var text = html.replace(/<(?:.|\s)*?>/g, "");
console.log(text); // 我来测试下by 龙恩

如上:我们来分析下:正则/<(?:.|\s)?>/g 的含义是:g是修饰符,全局匹配的含义;使用非捕获性分组?: 的含义是 子表达式可以作为被整体修饰但是子表达式匹配的结果不会被存储;因此:正则/<(?:.|\s)?>/g 的含义变为:匹配以< 开头 及 > 结束的所有字符;(?:.|\s)? 含义是:. 代表任意字符,| 含义是或者的意思,\s 是匹配空格的意思;号修饰符的含义是零个或者多个的意思;后面的?(问号)代表可匹配,可不匹配的含义;优先是可匹配;总起来的意思是:全局匹配字符串html 中的 以<开头 以>结尾的所有字符 替换成 空字符串,因此留下来就是文本;当然我们使用捕获性分组也可以得到同样的结果~

反向引用详细讲解

捕获性分组取到的内容,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用方式就叫做反向引用。
反向引用的作用是:是用来查找或限定重复,查找或限定指定标识配对出现等。
捕获性分组的反向引用的写法如:\number
Number是十进制数字,即捕获组的编号。
反向引用的匹配原理
捕获分组在匹配成功时,会将子表达式匹配到的内容,保存到内存中一个以数字编号的组里,可以简单的认为是对一个局部变量进行了赋值,这时就可以通过反向引用,引用这个局部变量的值。一个捕获分组在匹配成功之前,它的内容可以是不确定的,一旦匹配成功了,它的内容就确定了,反向引用的内容也就确定了。

比如如下代码:

var str = "longenaabcd";
console.log(str.match(/([ab])\1/)[0]);//aa

代码分析:对于如上代码中的正则 /([ab])\1/, 捕获组中子表达式[ab];可以匹配a,也可以匹配b,但是如果匹配成功的话,那么它的反向引用也就确定了,如果捕获分组匹配到的是a,那么它的反向引用就只能匹配a,如果捕获分组匹配到的是b,那么它的反向引用就只能匹配到b;\1的含义是 捕获分组匹配到是什么,那么它必须与捕获分组到是相同的字符;也就是说 只能匹配到aa或者bb才能匹配成功;

该正则匹配的过程我们可以来分析下:

字符串匹配正则/([ab])\1/, 在位置0处开始匹配,0处字符是l,很明显不满足,把控制权就交给下一个字符,一直到第6个字符,才匹配到a,匹配成功,把控制权交给\1, 也就是反向引用和分组中是相同的字符,因此也匹配a,字符串中下一个字符也是a,因此匹配成功,因此整个表达式找到匹配的字符,匹配的位置开始于6,结束与8;我们再可以匹配b,原理和上面一样,这里就不再多解释了;

转载: http://www.cnblogs.com/tugenhua0707/p/5037811.html

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

推荐阅读更多精彩内容