在JavaScript正則表達式(1)中,我們學習了如何聲明一個正則對象以及正則里常用的一些元字符奋救,正則對象的方法和字符串里的正則方法岭参。下面,我們來一起學習入門拓展的JavaScript正則表達式
正則表達式的反向引用###
在正則表達式中尝艘,括號能有‘記住’他們包含的子表達式匹配的文本演侯。所以,如果我們運用反向引用
的方法去匹配類似于AA背亥,疊字類型的文本秒际,就簡單很多了,當然狡汉,反向引用的作用不止如此娄徊。具體代碼如下:
var reg =/(c.(t))(123)\1\2\3/ig;
var text = '------cat123catt123-------';
text.match(reg);//["cat123catt123"]
\1
是匹配第一個括號里面的,\2
是匹配第一個括號里面嵌套的括號盾戴,\3
是匹配最后一個括號寄锐。
正則表達式的分組匹配###
分組匹配運用括號的原理和反向引用類似,只是反向引用作用在正則表達式‘內(nèi)部’尖啡,分組匹配在正則表達式‘外部’橄仆,語言的形容是蒼白無力的,請看下面的具體代碼:
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
在上述衅斩,我們知道可以用()
保留我們想要的數(shù)據(jù)盆顾,并通過一些方法,比如$1
畏梆,\1
取得您宪,如果你不想讓()
保留,可以使用(?:···)
的方式奠涌,讓這個括號變?yōu)?strong>非捕獲性括號蚕涤。這樣,正則就不會記錄里面的數(shù)據(jù)了铣猩。
正則表達式中的環(huán)視(lookaround)###
在了解環(huán)視之前揖铜,我們一起來思考一個問題,比如有這么一串數(shù)字28000000000
达皿,看起來是不是很累天吓?所以一般情況下,我們需要給它加上,
峦椰,就像這樣28,000,000,000
龄寞,但是問題來了,應該如何給這個字符串加上,
呢汤功,我們知道是從右往左物邑,一次3個數(shù)字,加一個,
,但是正則表達式都是從左往右解析的色解。聰明的你一定想到了茂嗓,我們以左邊至少有一個數(shù)字,右邊是3的倍數(shù)個數(shù)字的規(guī)則去匹配科阎,不就行了嗎述吸?所以,我們嘗試寫下了如下的正則表達式:
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
很顯然锣笨,我們得到了我們想要的結(jié)果蝌矛,但是是不是顯得不太優(yōu)雅?至少我認為是的错英,分析一下代碼入撒,我們發(fā)現(xiàn),正則匹配是只要匹配過去之后就不會回頭再去匹配(下一次匹配開始的地方是上一次匹配結(jié)束的地方)椭岩,所以要通過一個while
循環(huán)茅逮,一直遍歷到無法匹配為止。
所以簿煌,在這個時候,環(huán)視的概念就顯得尤為重要鉴吹,它的概念是什么呢姨伟?
環(huán)視是不匹配任何字符,只匹配文本中的特定位置的一種結(jié)構(gòu)豆励。與^
夺荒,\b
,$
有點類似良蒸。既然是一種結(jié)構(gòu)技扼,那么理所應當?shù)模?strong>匹配的時候并不會占用字符嫩痰。
不占用字符是一種怎么樣的概念呢剿吻?具體代碼如下:
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
通過上述代碼,應該很容易就可以發(fā)現(xiàn)串纺,環(huán)視的作用丽旅,使用環(huán)視的方式就是(?=···)
,而moon(?=burn)
的意思就是說纺棺,匹配一個moon
然后后面是burn
的字符串榄笙,moon(?:burn)
的意思其實和moon(?=burn)
理解起來是差不多的,但是還是有一個很大的區(qū)別祷蝌,就是上面提出的茅撞,用環(huán)視的方式進行匹配,匹配的字符本身不會被算為占用的字符,也就是說米丘,用moon(?=burn)
剑令,匹配到n
的時候,遇到了環(huán)視蠕蚜,然后判斷后續(xù)是否符合環(huán)視的規(guī)則尚洽,如果符合,返回moon
靶累,如果不符合腺毫,返回null
,而moon(?:burn)
的意思是挣柬,匹配到n
之后潮酒,繼續(xù)向后匹配b
,u
邪蛔,r
急黎,n
,如果都匹配成功,返回moonburn
(因為burn也是匹配項)侧到,失敗勃教,返回null
。這就是環(huán)視最大的優(yōu)點匹配的時候并不會占用字符匠抗。
環(huán)視的種類####
當然故源,為了需要,環(huán)視也不僅僅只有(?=···)
一種汞贸,還有以下的:
類型 | 正則表達式 | 匹配成功的條件 |
---|---|---|
肯定順序環(huán)視 | (?=···) | 子表達式能夠匹配右側(cè)文本 |
否定順序環(huán)視 | (?!···) | 子表達式不能匹配右側(cè)文本 |
環(huán)視大家差不多已經(jīng)有所了解了绳军,那么回到一開始的那個例子,通過環(huán)視矢腻。如何更優(yōu)雅的給一串數(shù)字加上,
呢门驾?具體代碼如下:
var reg =/([0-9])(?=([0-9][0-9][0-9])+(?![0-9]))/g;
var text = '28000000000';
text.replace(reg,'$1,');//28,000,000,000
是不是通過環(huán)視就把while
循環(huán)去掉了,變得清爽了很多多柑?確實奶是,環(huán)視的作用確實蠻大的,希望大家能夠理解竣灌,熟練運用并掌握诫隅。
字符組簡記法
在上述例子中,我們用[0-9]
來匹配一個數(shù)字帐偎,其實在JavaScript中(很多其他語言也是一樣的)逐纬,用\d
來表示匹配一個數(shù)字,所以削樊,上面的代碼可以改為:
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 |
數(shù)字 | 等價于[0-9]
|
\D |
非數(shù)字字符 | 等價于[^\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是傳統(tǒng)型的NFA
躁倒,不是POSIX NFA
。
在了解不同引擎的差異之前洒琢,我們可以先來了解一下它們的共同點秧秉。以下共同點適用于所有引擎:
- 優(yōu)先選擇最左端(最開頭)的位置匹配。
- 標準的匹配量詞
*
,+
,?
和{m.n}
是匹配優(yōu)先的衰抑。
下面 開始討論第一條規(guī)則象迎,優(yōu)先選擇最左端(最開頭)的位置匹配:
舉個例子,例如以下情況:
var reg = /cat/g;
var text = '123catt45678cat'
text.search(reg)//3
在text
中停士,滿足reg
的位置有2個挖帘,分別是3
,12
完丽,因為優(yōu)先選擇左端的位置恋技,所以匹配了3
位置的cat
。
再來一個例子:
var reg = /cat|dog|monkey/g;
var text = '123monkeydogcatt45678cat'
text.search(reg)//3
這個例子很簡單逻族,告訴我們蜻底,正則表達式的匹配是所有的匹配項都會嘗試一遍,哪怕monkey
項在正則的最后面聘鳞,也會最先匹配薄辅。
下面開始討論第二條規(guī)則,標準的匹配量詞*
,+
,?
和{m.n}
是匹配優(yōu)先的抠璃。
關于上述的匹配量詞站楚,都是匹配優(yōu)先的,匹配優(yōu)先是個什么意思呢搏嗡?比如窿春,當我們用了+
來進行匹配的時候拉一,當匹配到一個滿足條件之后,他會繼續(xù)往下匹配旧乞,一直匹配到無法匹配為止蔚润。如果我們看到匹配出來的結(jié)果不是最大匹配項,那么一定是因為最大匹配項的情況下無法滿足后續(xù)的匹配規(guī)則尺栖,就減少匹配項從而滿足后續(xù)項嫡纠,這種全部匹配的過程,就是匹配優(yōu)先的過程延赌。舉個例子:
var reg = /[0-9]+0/g;
var text = '12340567890123';
text.match(reg);//["12340567890"]
這個是匹配成功了除盏,一開始的[0-9]+
就把1234567890123
全部匹配完了(匹配優(yōu)先,就是這么厲害皮胡!不服不行3占铡),但是匹配完了之后屡贺,發(fā)現(xiàn)無法滿足后面的匹配項0
,沒有辦法蠢棱,只能退回一個,看看能不能滿足甩栈,于是[0-9]+
的匹配項變成了``123456789012泻仙,發(fā)現(xiàn)還是不行,重復上述過程······一直退回到
123456789量没,后面匹配到
0,滿足了玉转!搞定收工!返回了
["12340567890"]殴蹄,細心的你究抓,一定是發(fā)現(xiàn)了,其實在前面的
12340也是滿足這個正則的袭灯,并且是排在了更加靠左的地方刺下,但是卻被忽略了,為什么稽荧?因為有關匹配量詞的引擎內(nèi)部的計算方法就是這樣的橘茉,或者說因為**匹配優(yōu)先**,而選擇了后面的符合條件的
1234567890`姨丈。
為了鞏固大家對匹配優(yōu)先的理解畅卓,我準備趁熱打鐵,再來一個可能大家會混淆的例子(希望是我多慮了):
var reg = /[0-9]+([0-9]+)/g;
var text = '12340567890123';
text.match(reg);
RegExp.$1;//是多少呢蟋恬?
先允許我賣個關子翁潘,先來分析一波,想一下匹配優(yōu)先的原則歼争,一開始的[0-9]+
把text
全部匹配完了拜马,然后慢慢退回箱歧,退回一個3
給([0-9]+)
之后,滿足了一膨,完成走人......可能有人會想呀邢,后面一個也是+
啊為什么才給它一個數(shù)字呢淮悼,沒辦法咱筛,先來先服務嘛约谈,勉強給你一個滿足就不錯了撤逢,你還要幾個捡鱼?還要啥自行車碘裕?所以RegExp.$1
的值是3
更啄。
答對了嗎诫给?如果答對了巷蚪,那你應該對匹配優(yōu)先了解透徹了病毡,如果沒有答對或者很猶豫,那請你在看看前文吧屁柏。因為萬丈高樓平地起啦膜,很多復雜的正則表達式都是從這個開始的√视鳎基礎尤為重要僧家。
JavaScript 正則表達式(1)
JavaScript 正則表達式(2)
JavaScript 正則表達式(3)
JavaScript 正則表達式(4)