RegExp 三大方法
本文的RegExp采用直接量語法表示:/pattern/attributes。attributes有三個選擇,i、m和g,m(多行匹配)不常用直接省略,所以一個pattern(匹配模式)可以表示如下:
var pattern = /hello/ig;
i(ignore)表示不區(qū)分大小寫(地搜索匹配),比較簡單,以下例子中不加述說;g(global)表示全局(搜索匹配),即找到一個后繼續(xù)找下去,相對復(fù)雜,以下各種方法中會特別介紹。
既然是RegExp的三大方法,所以都是pattern.test/exec/complie的格式。
test
主要功能:檢測指定字符串是否含有某個子串(或者匹配模式),返回true或者false。
示例如下:
var s = 'you love me and I love you'; var pattern = /you/; var ans = pattern.test(s); console.log(ans); // true
如果attributes用了g,則可以繼續(xù)找下去,其中還會涉及l(fā)astIndex屬性(參照exec中搭配g的介紹)。
exec
主要功能:提取指定字符串中的符合要求的子串(或者匹配模式),返回一個數(shù)組存放匹配結(jié)果;如果沒有,則返回null。(也可自己寫方法循環(huán)提取所有或者指定index的數(shù)據(jù))
exec可以說是test的升級版本,因為它不僅可以檢測,而且檢測到了可以直接提取結(jié)果。
示例如下:
var s = 'you love me and I love you'; var pattern = /you/; var ans = pattern.exec(s); console.log(ans); // ["you", index: 0, input: "you love me and I love you"] console.log(ans.index); // 0 console.log(ans.input); // you love me and I love you
輸出的東西很有意思。此數(shù)組的第 0 個元素是與正則表達(dá)式相匹配的文本,第 1 個元素是與 RegExpObject 的第 1 個子表達(dá)式相匹配的文本(如果有的話),第 2 個元素是與 RegExpObject 的第 2 個子表達(dá)式相匹配的文本(如果有的話),以此類推。
啥叫“與子表達(dá)式相匹配的文本”?看下面的例子:
var s = 'you love me and I love you'; var pattern = /y(o?)u/; var ans = pattern.exec(s); console.log(ans); // ["you", "o", index: 0, input: "you love me and I love you"] console.log(ans.length) // 2
所謂的子表達(dá)式就是pattern里()內(nèi)的東西(具體可以參考下文對子表達(dá)式的介紹)。再看上面例子的數(shù)組長度,是2??!index和input只是數(shù)組屬性(chrome中以上的輸出可能會讓人誤會)。
除了數(shù)組元素和 length 屬性之外,exec() 方法還返回兩個屬性。index 屬性聲明的是匹配文本的第一個字符的位置。input 屬性則存放的是被檢索的字符串 string。我們可以看得出,在調(diào)用非全局的 RegExp 對象的 exec() 方法時,返回的數(shù)組與調(diào)用方法 String.match() 返回的數(shù)組是相同的。
如果使用 “g” 參數(shù),exec() 的工作原理如下(還是以上的例子 ps:如果test使用g參數(shù)類似):
找到第一個 “you”,并存儲其位置
如果再次運行 exec(),則從存儲的位置(lastIndex)開始檢索,并找到下一個 “you”,并存儲其位置
當(dāng) RegExpObject 是一個全局正則表達(dá)式時,exec() 的行為就稍微復(fù)雜一些。它會在 RegExpObject 的 lastIndex 屬性指定的字符處開始檢索字符串 string。當(dāng) exec() 找到了與表達(dá)式相匹配的文本時,在匹配后,它將把 RegExpObject 的 lastIndex 屬性設(shè)置為匹配文本的最后一個字符的下一個位置。這就是說,我們可以通過反復(fù)調(diào)用 exec() 方法來遍歷字符串中的所有匹配文本。當(dāng) exec() 再也找不到匹配的文本時,它將返回 null,并把 lastIndex 屬性重置為 0。這里引入lastIndex屬性,這貨只有跟g和test(或者g和exec)三者搭配時才有作用。它是pattern的一個屬性,一個整數(shù),標(biāo)示開始下一次匹配的字符位置。
實例如下:
var s = 'you love me and I love you'; var pattern = /you/g; var ans; do { ans = pattern.exec(s); console.log(ans); console.log(pattern.lastIndex); } while (ans !== null)
結(jié)果如下:
[圖片上傳中。。。(1)]
應(yīng)該還容易理解,當(dāng)?shù)谌窝h(huán)時,找不到“you”了,于是返回null,lastIndex值也變成0了。
如果在一個字符串中完成了一次模式匹配之后要開始檢索新的字符串(仍然使用舊的pattern),就必須手動地把 lastIndex 屬性重置為 0。
compile
主要功能:改變當(dāng)前匹配模式(pattern)
這貨是改變匹配模式時用的,用處不大,略過。詳見JavaScript compile() 方法
String 四大護法
和RegExp三大方法分庭抗禮的是String的四大護法,四大護法有些和RegExp三大方法類似,有的更勝一籌。
既然是String家族下的四大護法,所以肯定是string在前,即str.search/match/replace/split形式。
既然是String的方法,當(dāng)然參數(shù)可以只用字符串而不用pattern。
search
主要功能:搜索指定字符串中是否含有某子串(或者匹配模式),如有,返回子串在原串中的初始位置,如沒有,返回-1。
是不是和test類似呢?test只能判斷有木有,search還能返回位置!當(dāng)然test()如果有需要能繼續(xù)找下去,而search則會自動忽略g(如果有的話)。實例如下:
var s = 'you love me and I love you'; var pattern = /you/; var ans = s.search(pattern); console.log(ans); // 0
話說和String的indexOf方法有點相似,不同的是indexOf方法可以從指定位置開始查找,但是不支持正則。
match
主要功能:和exec類似,從指定字符串中查找子串或者匹配模式,找到返回數(shù)組,沒找到返回null
match是exec的輕量版,當(dāng)不使用全局模式匹配時,match和exec返回結(jié)果一致;當(dāng)使用全局模式匹配時,match直接返回一個字符串?dāng)?shù)組,獲得的信息遠(yuǎn)沒有exec多,但是使用方式簡單。
實例如下:
var s = 'you love me and I love you'; console.log(s.match(/you/)); // ["you", index: 0, input: "you love me and I love you"] console.log(s.match(/you/g)); // ["you", "you"]
replace
主要功能:用另一個子串替換指定字符串中的某子串(或者匹配模式),返回替換后的新的字符串 str.replace(‘搜索模式’,’替換的內(nèi)容’) 如果用的是pattern并且?guī),則全部替換;否則替換第一處。
實例如下:
var s = 'you love me and I love you'; console.log(s.replace('you', 'zichi')); // zichi love me and I love you console.log(s.replace(/you/, 'zichi')); // zichi love me and I love you console.log(s.replace(/you/g, 'zichi')); // zichi love me and I love zichi
如果需要替代的內(nèi)容不是指定的字符串,而是跟匹配模式或者原字符串有關(guān),那么就要用到$了(記住這些和$符號有關(guān)的東東只和replace有關(guān)哦)。
[圖片上傳中。。。(2)]
怎么用?看個例子就明白了。
var s = 'I love you'; var pattern = /love/; var ans = s.replace(pattern, '$' + '$&' + "$'"); console.log(ans); // I I love you you 沒錯,’$’ + ‘$&’ + “$’”其實就相當(dāng)于原串了!
replace的第二個參數(shù)還能是函數(shù),看具體例子前先看一段介紹:
注意:第一個參數(shù)是匹配到的子串,接下去是子表達(dá)式匹配的值,如果要用子表達(dá)式參數(shù),則必須要有第一個參數(shù)(表示匹配到的串),也就是說,如果要用第n個參數(shù)代表的值,則左邊參數(shù)都必須寫出來。最后兩個參數(shù)跟exec后返回的數(shù)組的兩個屬性差不多。
var s = 'I love you'; var pattern = /love/; var ans = s.replace(pattern, function(a) { // 只有一個參數(shù),默認(rèn)為匹配到的串(如還有參數(shù),則按序表示子表達(dá)式和其他兩個參數(shù)) return a.toUpperCase(); }); console.log(ans); // I LOVE you
split
主要功能:分割字符串
字符串分割成字符串?dāng)?shù)組的方法(另有數(shù)組變成字符串的join方法)。直接看以下例子:
var s = 'you love me and I love you'; var pattern = 'and'; var ans = s.split(pattern); console.log(ans); // ["you love me ", " I love you"]
如果你嫌得到的數(shù)組會過于龐大,也可以自己定義數(shù)組大小,加個參數(shù)即可:
var s = 'you love me and I love you'; var pattern = /and/; var ans = s.split(pattern, 1); console.log(ans); // ["you love me "]
RegExp 字符
[圖片上傳中。。。(3)]
[圖片上傳中。。。(4)]
[圖片上傳中。。。(5)]
\s 任意空白字符 \S相反 空白字符可以是: 空格符 (space character) 制表符 (tab character) 回車符 (carriage return character) 換行符 (new line character) 垂直換行符 (vertical tab character) 換頁符 (form feed character)
\b是正則表達(dá)式規(guī)定的一個特殊代碼,代表著單詞的開頭或結(jié)尾,也就是單詞的分界處。雖然通常英文的單詞是由空格,標(biāo)點符號或者換行來分隔的,但是\b并不匹配這些單詞分隔字符中的任何一個,它只匹配一個位置。(和^ $ 以及零寬斷言類似)
\w 匹配字母或數(shù)字或下劃線 [a-z0-9A-Z_]完全等同于\w
貪婪匹配和懶惰匹配
什么是貪婪匹配?貪婪匹配就是在正則表達(dá)式的匹配過程中,默認(rèn)會使得匹配長度越大越好。
var s = 'hello world welcome to my world'; var pattern = /hello.world/; var ans = pattern.exec(s); console.log(ans) // ["hello world welcome to my world", index: 0, input: "hello world welcome to my world"]
以上例子不會匹配最前面的hello world,而是一直貪心的往后匹配。
那么我需要最短的匹配怎么辦?很簡單,加個‘?’即可,這就是傳說中的懶惰匹配,即匹配到了,就不往后找了。
var s = 'hello world welcome to my world'; var pattern = /hello.?world/; var ans = pattern.exec(s); console.log(ans) // ["hello world", index: 0, input: "hello world welcome to my world"]
懶惰限定符(?)添加的場景如下:
[圖片上傳中。。。(6)]
子表達(dá)式
表示方式
用一個小括號指定:
var s = 'hello world'; var pattern = /(hello)/; var ans = pattern.exec(s); console.log(ans);
子表達(dá)式出現(xiàn)場景
在exec中數(shù)組輸出子表達(dá)式所匹配的值:
var s = 'hello world'; var pattern = /(h(e)llo)/; var ans = pattern.exec(s); console.log(ans); // ["hello", "hello", "e", index: 0, input: "hello world"]
在replace中作為替換值引用:
var s = 'hello world'; var pattern = /(h\wo)\s(w\w*d)/; var ans = s.replace(pattern, '$2 $1') console.log(ans); // world hello
后向引用 & 零寬斷言
子表達(dá)式的序號問題
簡單地說:從左向右,以分組的左括號為標(biāo)志,第一個出現(xiàn)的分組的組號為1,第二個為2,以此類推。
復(fù)雜地說:分組0對應(yīng)整個正則表達(dá)式實際上組號分配過程是要從左向右掃描兩遍的:第一遍只給未命名組分配,第二遍只給命名組分配--因此所有命名組的組號都大于未命名的組號。可以使用(?:exp)這樣的語法來剝奪一個分組對組號分配的參與權(quán).
后向引用
如果我們要找連續(xù)兩個一樣的字符,比如要找兩個連續(xù)的c,可以這樣/c{2}/,如果要找兩個連續(xù)的單詞hello,可以這樣/(hello){2}/,但是要在一個字符串中找連續(xù)兩個相同的任意單詞呢,比如一個字符串hellohellochinaworldworld,我要找的是hello和world,怎么找?
這時候就要用后向引用。看具體例子:
var s = 'hellohellochinaworldworld'; var pattern = /(\w+)\1/g; var a = s.match(pattern); console.log(a); // ["hellohello", "worldworld"]
這里的\1就表示和匹配模式中的第一個子表達(dá)式(分組)一樣的內(nèi)容,\2表示和第二個子表達(dá)式(如果有的話)一樣的內(nèi)容,\3 \4 以此類推。(也可以自己命名,詳見參考文獻(xiàn))
或許你覺得數(shù)組里兩個hello兩個world太多了,我只要一個就夠了,就又要用到子表達(dá)式了。因為match方法里是不能引用子表達(dá)式的值的,我們回顧下哪些方法是可以的?沒錯,exec和replace是可以的!
exec方式:
var s = 'hellohellochinaworldworld'; var pattern = /(\w+)\1/g; var ans; do { ans = pattern.exec(s); console.log(ans); } while(ans !== null); // result // ["hellohello", "hello", index: 0, input: "hellohellochinaworldworld"] index.html:69 // ["worldworld", "world", index: 15, input: "hellohellochinaworldworld"] index.html:69 // null
如果輸出只要hello和world,console.log(ans[1])即可。
replace方式:
var s = 'hellohellochinaworldworld'; var pattern = /(\w+)\1/g; var ans = []; s.replace(pattern, function(a, b) { ans.push(b); }); console.log(ans); // ["hello", "world"]
如果要找連續(xù)n個相同的串,比如說要找出一個字符串中出現(xiàn)最多的字符:
String.prototype.getMost = function() { var a = this.split(''); a.sort(); var s = a.join(''); var pattern = /(\w)\1*/g; var a = s.match(pattern); a.sort(function(a, b) { return a.length < b.length; }); var letter = a[0][0]; var num = a[0].length; return letter + ': ' + num; } var s = 'aaabbbcccaaabbbcccccc'; console.log(s.getMost()); // c: 9
如果需要引用某個子表達(dá)式(分組),請認(rèn)準(zhǔn)后向引用!
零寬斷言
別被名詞嚇壞了,其實解釋很簡單。
它們用于查找在某些內(nèi)容(但并不包括這些內(nèi)容)之后的東西,也就是說它們像\b,^,$那樣用于指定一個位置,這個位置應(yīng)該滿足一定的條件(即斷言)
(?=exp)
零寬度正預(yù)測先行斷言,它斷言自身出現(xiàn)的位置的后面能匹配表達(dá)式exp。
// 獲取字符串中以ing結(jié)尾的單詞的前半部分 var s = 'I love dancing but he likes singing'; var pattern = /\b\w+(?=ing\b)/g; var ans = s.match(pattern); console.log(ans); // ["danc", "sing"]
(?!exp)
零寬度負(fù)預(yù)測先行斷言,斷言此位置的后面不能匹配表達(dá)式exp
// 獲取第五位不是i的單詞的前四位 var s = 'I love dancing but he likes singing'; var pattern = /\b\w{4}(?!i)/g; var ans = s.match(pattern); console.log(ans); // ["love", "like"]
javascript正則只支持前瞻,不支持后瞻((?<=exp)和(?<!exp))。
關(guān)于零寬斷言的具體應(yīng)用可以參考綜合應(yīng)用一節(jié)給字符串加千分符。
其他
字符轉(zhuǎn)義
因為某些字符已經(jīng)被正則表達(dá)式用掉了,比如. * ( ) / \ [],所以需要使用它們(作為字符)時,需要用\轉(zhuǎn)義
var s = 'http://www.cnblogs.com/zichi/'; var pattern = /http://www.cnblogs.com/zichi//; var ans = pattern.exec(s); console.log(ans); // ["http://www.cnblogs.com/zichi/", index: 0, input: "http://www.cnblogs.com/zichi/"]
分支條件
如果需要匹配abc里的任意字母,可以用[abc],但是如果不是單個字母那么簡單,就要用到分支條件。
分支條件很簡單,就是用|表示符合其中任意一種規(guī)則。
var s = "I don't like you but I love you"; var pattern = /I.(like|love).you/g; var ans = s.match(pattern); console.log(ans); // ["I don't like you but I love you"]
答案執(zhí)行了貪婪匹配,如果需要懶惰匹配,則:
var s = "I don't like you but I love you"; var pattern = /I.?(like|love).?you/g; var ans = s.match(pattern); console.log(ans); // ["I don't like you", "I love you"]
綜合應(yīng)用
去除字符串首尾空格(replace)
String.prototype.trim = function() { return this.replace(/(^\s)|(\s$)/g, ""); }; var s = ' hello world '; var ans = s.trim(); console.log(ans.length); // 12
給字符串加千分符(零寬斷言)
String.prototype.getAns = function() { var pattern = /(?=((?!\b)\d{3})+$)/g; return this.replace(pattern, ','); } var s = '123456789'; console.log(s.getAns()); // 123,456,789
找出字符串中出現(xiàn)最多的字符(后向引用)
String.prototype.getMost = function() { var a = this.split(''); a.sort(); var s = a.join(''); var pattern = /(\w)\1*/g; var a = s.match(pattern); a.sort(function(a, b) { return a.length < b.length; }); var letter = a[0][0]; var num = a[0].length; return letter + ': ' + num; } var s = 'aaabbbcccaaabbbcccccc'; console.log(s.getMost()); // c: 9
常用匹配模式(持續(xù)更新)
只能輸入漢字:/^[\u4e00-\u9fa5]{0,}$/
總結(jié)
test:檢查指定字符串中有沒有某子串(或某匹配模式),返回true或者false;如有必要可以進(jìn)行全局模式搜索。
exec:檢查指定字符串中有沒有某子串(或者匹配模式),如有返回數(shù)組(數(shù)組信息豐富,可參考上文介紹),如沒有返回null;如有必要可以進(jìn)行全局搜索找出所有子串(或者匹配模式)的信息,信息中含有匹配模式中子表達(dá)式所對應(yīng)的字符串。
compile:修改正則表達(dá)式中的pattern
search:檢查指定字符串中有沒有某子串(或者匹配模式),如有返回子串(或者匹配模式)在原串中的開始位置,如沒有返回-1。不能進(jìn)行全局搜索。
match:檢查指定字符串中有沒有某子串(或者匹配模式),非全局模式下返回信息和exec一致;如進(jìn)行全局搜索,直接返回字符串?dāng)?shù)組。(如不需要關(guān)于每個匹配的更多信息,推薦用match而不是exec)
replace:檢查指定字符串中有沒有某子串(或者匹配模式),并用另一個子串代替(該子串可以跟原字符串或者搜索到的子串有關(guān));如啟動g,則全局替換,否則只替換第一個。replace方法可以引用子表達(dá)式所對應(yīng)的值。
split:用特定模式分割字符串,返回一個字符串?dāng)?shù)組;與Array的join方法正好相反。
子表達(dá)式:用括號括起來的正則匹配表達(dá)式,用后向引用可以對其進(jìn)行引用;也可以和exec或者replace搭配獲取其真實匹配值。
后向引用 :對子表達(dá)式所在分組進(jìn)行引用。
零寬斷言:和\b ^ 以及$類似的某個位置概念。