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 >我來測試下</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閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容