JavaScript種正則表達式有兩種定義方式,定義一個匹配類似 <%XXX%> 的字符串
1. 構造函數
var reg=new RegExp('<%[^%>]+%>','g');
2. 字面量
var reg=/<%[^%>]%>/g;
正則表達式讓人望而卻步以一個重要原因就是其轉義字符太多了,組合非常之多,但是正則表達式的元字符(在正則表達式中具有特殊意義的專用字符,可以用來規定其前導字符)並不多
元字符:( [ { \ ^ $ | ) ? * + .
並不是每個元字符都有其特定意義,在不同的組合中元字符有不同的意義,分類看一下
一般情況下正則表達式一個字符(轉義字符算一個)對應字符串一個字符,表達式 ab\t 的含義是
但是我們可以使用元字符[]來構建一個簡單的類,所謂類是指,符合某些特征的對象,是一個泛指,而不是特指某個字符了,我們可以使用表達式 [abc] 把字符a或b或c歸為一類,表達式可以匹配這類的字符
元字符[]組合可以創建一個類,我們還可以使用元字符^創建反向類/負向類,反向類的意思是不屬於XXX類的內容,表達式 [^abc] 表示不是字符a或b或c的內容
按照上面的說明要是我們希望匹配單個數字那麼表達式是這樣的
[0123456789]
如果是字母那麼。。。,好麻煩,正則表達式還提供了范圍類,我們可以使用 x-y來連接兩個字符表示從x到y的任意字符,這是個閉區間,也就是說包含x和ybenshen,這樣匹配小寫字母就很簡單了
[a-z]
要是想匹配所有字母呢?在[]組成的類內部是可以連寫的,我們還可以這樣寫 [a-zA-Z]
剛才使用正則我們創建了幾個類,來表示數字,字母等,但這樣寫也很是麻煩,正則表達式為我們提供了幾個常用的預定義類來匹配常見的字符
字符 等價類 含義 . [^\n\r] 除了回車符和換行符之外的所有字符 \d [0-9] 數字字符 \D [^0-9] 非數字字符 \s [ \t\n\x0B\f\r] 空白符 \S [^ \t\n\x0B\f\r] 非空白符 \w [a-zA-Z_0-9] 單詞字符(字母、數字、下劃線) \W [^a-zA-Z_0-9] 非單詞字符
有了這些預定義類,寫一些正則就很方便了,比如我們希望匹配一個 ab+數字+任意字符 的字符串,就可以這樣寫了 ab\d.
正則表達式還提供了幾個常用的邊界匹配字符
字符
含義
^
以xx開頭
$
以xx結尾
\b
單詞邊界,指[a-zA-Z_0-9]之外的字符
\B
非單詞邊界
看個不負責任的郵箱正則匹配(切勿模仿,小括號後面會講到) \w+@\w+\.(com)$
之前我們介紹的方法都是一一匹配的,如果我們希望匹配一個連續出現20次數字的字符串難道我們需要寫成這樣
\d\d\d\d...
為此正則表達式引入了一些量詞
字符 含義 ? 出現零次或一次(最多出現一次) + 出現一次或多次(至少出現一次) * 出現零次或多次(任意次) {n} 出現n次 {n,m} 出現n到m次 {n,} 至少出現n次
看幾個使用量詞的例子
\w+\b Byron 匹配 單詞+邊界+Byron
(/\w+\b Byron/).test('Hi Byron'); //true (/\w+\b Byron/).test('Welcome Byron'); //true (/\w+\b Byron/).test('HiByron'); //false
\d+\.\d{1,3} 匹配三位小數的數字
看了上面介紹的量詞,也許愛思考的同學會想到關於匹配原則的一些問題,比如{3,5}這個量詞,要是在句子種出現了十次,那麼他是每次匹配三個還是五個,反正3、4、5都滿足3~5的條件,量詞在默認下是盡可能多的匹配的,也就是大家常說的貪婪模式
'123456789'.match(/\d{3,5}/g); //["12345", "6789"]
既然有貪婪模式,那麼肯定會有非貪婪模式,讓正則表達式盡可能少的匹配,也就是說一旦成功匹配不不再繼續嘗試,做法很簡單,在量詞後加上 ? 即可
'123456789'.match(/\d{3,5}?/g); //["123", "456", "789"]
有時候我們希望使用量詞的時候匹配多個字符,而不是像上面例子只是匹配一個,比如希望匹配Byron出現20次的字符串,我們如果寫成 Byron{20} 的話匹配的是Byro+n出現20次,怎麼把Byron作為一個整體呢?使用()就可以達到次目的,我們稱為分組
(Byron){20}
如果希望匹配Byron或Casper出現20次該怎麼辦呢?可以使用字符 | 達到或的功效
(Byron|Casper){20}
我們看到圖中有個#1的東東,那是什麼?使用分組的正則表達式會把匹配項也放到分組中,默認就是按數字編號分發的,各異根據編號獲得捕獲的分組內容,這個在一些希望具體操作第幾個匹配項的函數中很有用
(Byron).(ok)
如果有分組嵌套的情況,外面的組的編號靠前
((^|%>)[^\t]*)
有時候我們不希望捕獲某些分組,只需要在分組內加上 ?: 就可以了,著並不意味著該分組內容不屬於正則表達式,只是不會給這個分組加編號了而已
(?:Byron).(ok)
其實在C#等語言中分組還可以起名字,不過JavaScript不支持
說的有些抽象,看個例子 good(?=Byron)
(/good(?=Byron)/).exec('goodByron123'); //['good'] (/good(?=Byron)/).exec('goodCasper123'); //null (/bad(?=Byron)/).exec('goodCasper123');//null
通過上面例子可以看出 exp1(?=exp2) 表達式會匹配exp1表達式,但只有其後面內容是exp2的時候才會匹配,也就是兩個條件,exp1(?!exp2) 比較類似
good(?!Byron)
(/good(?!Byron)/).exec('goodByron123'); //null (/good(?!Byron)/).exec('goodCasper123'); //['good'] (/bad(?!Byron)/).exec('goodCasper123');//null
上面介紹了JavaScript正則表達式的語法,有了這些基本知識,可以看看正則表達式在JavaScript的應用了,在一切開始之前,看看RegExp實例的幾個屬性
RegExp實例對象有五個屬性
除了將正則表達式編譯為內部格式從而使執行更快的compile()方法,對象還有兩個我們常用的方法
方法用於測試字符串參數中是否存正則表達式模式,如果存在則返回true,否則返回false
var reg=/\d+\.\d{1,2}$/g; reg.test('123.45'); //true reg.test('0.2'); //true reg.test('a.34'); //false reg.test('34.5678'); //false
方法用於正則表達式模式在字符串中運行查找,如果 exec() 找到了匹配的文本,則返回一個結果數組。否則,返回 null。除了數組元素和 length 屬性之外,exec() 方法還返回兩個屬性。index 屬性聲明的是匹配文本的第一個字符的位置。input 屬性則存放的是被檢索的字符串 string。
調用非全局的 RegExp對象的 exec() 時,返回數組的第 0 個元素是與正則表達式相匹配的文本,第 1 個元素是與 RegExpObject 的第 1 個子表達式相匹配的文本(如果有的話),第 2 個元素是與 RegExp對象的第 2 個子表達式相匹配的文本(如果有的話),以此類推。
調用全局的RegExp對象的 exec() 時,它會在 RegExp實例的 lastIndex 屬性指定的字符處開始檢索字符串 string。當 exec() 找到了與表達式相匹配的文本時,在匹配後,它將把 RegExp實例的 lastIndex 屬性設置為匹配文本的最後一個字符的下一個位置。可以通過反復調用 exec() 方法來遍歷字符串中的所有匹配文本。當 exec() 再也找不到匹配的文本時,它將返回 null,並把 lastIndex 屬性重置為 0。
var reg=/\d/g; var r=reg.exec('a1b2c3'); console.log(reg.lastIndex); //2 r=reg.exec('a1b2c3'); console.log(reg.lastIndex); //4
兩次執行r的結果
var reg=/\d/g; while(r=reg.exec('a1b2c3')){ console.log(r.index+':'+r[0]); }可以看到結果:
1:1 3:2 5:3
除了上面的兩個方法,有些字符串函數可以傳入RegExp對象作為參數,進行一些復雜的操作
search() 方法用於檢索字符串中指定的子字符串,或檢索與正則表達式相匹配的子字符串。search() 方法不執行全局匹配,它將忽略標志 g。它同時忽略 regexp 的 lastIndex 屬性,並且總是從字符串的開始進行檢索,這意味著它總是返回 stringObject 的第一個匹配的位置。
'a1b2c3'.search(/\d/g); //1 'a1b2c3'.search(/\d/); //1
match() 方法將檢索字符串 stringObject,以找到一個或多個與 regexp 匹配的文本。但regexp是否具有標志 g對結果影響很大。
如果 regexp 沒有標志 g,那麼 match() 方法就只能在 strObj 中執行一次匹配。如果沒有找到任何匹配的文本, match() 將返回 null。否則,它將返回一個數組,其中存放了與它找到的匹配文本有關的信息。該數組的第 0 個元素存放的是匹配文本,而其余的元素存放的是與正則表達式的子表達式匹配的文本。除了這些常規的數組元素之外,返回的數組還含有兩個對象屬性。index 屬性聲明的是匹配文本的起始字符在 stringObject 中的位置,input 屬性聲明的是對 stringObject 的引用。
var r='aaa123456'.match(/\d/);
如果 regexp 具有標志 g,則 match() 方法將執行全局檢索,找到 strObj 中的所有匹配子字符串。若沒有找到任何匹配的子串,則返回 null。如果找到了一個或多個匹配子串,則返回一個數組。不過全局匹配返回的數組的內容與前者大不相同,它的數組元素中存放的是 strObj 中所有的匹配子串,而且也沒有 index 屬性或 input 屬性。
var r='aaa123456'.match(/\d/g);
關於strng對象的replace方法,我們最常用的時傳入兩個字符串的做法,但這種做法有個缺陷,只能replace一次
'abcabcabc'.replace('bc','X'); //aXabcabc
replace方法的第一個參數還可以傳入RegExp對象,傳入正則表達式可以時replace方法更加強大靈活
'abcabcabc'.replace(/bc/g,'X'); //aXaXaX 'abcaBcabC'.replace(/bc/gi,'X'); //aXaXaX
如果replace方法的第一個參數傳入的是帶分組的正則表達式,我們在第二個參數中可以使用$1...$9來獲取相應分組內容,比如希望把字符串 1<%2%>34<%567%>89 的<%x%>換為$#x#$,我們可以這樣
'1<%2%>34<%567%>89'.replace(/<%(\d+)%>/g,'@#$1#@'); //"1@#2#@34@#567#@89"
當然還有很多方式可以達到這一目的,這裡只是演示一下利用分組內容,我們在第二個參數中使用 @#$1#@,其中$1表示被捕獲的分組內容,在一些js模板函數中可以經常見到這種方式替換字符串。
可以通過修改replace方法的第二個參數,使replace更加強大,在前面的介紹中,只能把所有匹配替換為固定內容,但如果我希望把一個字符串中所有數字,都用小括號包起來該怎麼弄
'2398rufdjg9w45hgiuerhg83ghvif'.replace(/\d+/g,function(r){ return '('+r+')'; }); //"(2398)rufdjg(9)w(45)hgiuerhg(83)ghvif"
把replace方法的第二個參數傳入一個function,這個function會在每次匹配替換的時候調用,算是個每次替換的回調函數,我們使用了回調函數的第一個參數,也就是匹配內容,其實回調函數一共有四個參數
'2398rufdjg9w45hgiuerhg83ghvif'.replace(/\d+/g,function(a,b,c){ console.log(a+'\t'+b+'\t'+c); return '('+a+')'; }); 0 2398rufdjg9w45hgiuerhg83ghvif 10 2398rufdjg9w45hgiuerhg83ghvif 12 2398rufdjg9w45hgiuerhg83ghvif 22 2398rufdjg9w45hgiuerhg83ghvif
這是沒有分組的情況,打印出來的分別是 匹配內容、匹配項index和原字符串,看個有分組的例子,如果我們希望把一個字符串的<%%>外殼去掉,<%1%><%2%><%3%> 變成123
'<%1%><%2%><%3%>'.replace(/<%([^%>]+)%>/g,function(a,b,c,d){ console.log(a+'\t'+b+'\t'+c+'\t'+d); return b; }) //123 <%1%> 1 0 <%1%><%2%><%3%> <%2%> 2 5 <%1%><%2%><%3%> <%3%> 3 10 <%1%><%2%><%3%>
根據這種參數replace可以實現很多強大的功能,尤其是在復雜的字符串替換語句中經常使用。
我們經常使用split方法把字符串分割為字符數組
'a,b,c,d'.split(','); //["a", "b", "c", "d"]
和replace方法類似,在一些復雜的分割情況下我們可以使用正則表達式解決
'a1b2c3d'.split(/\d/); //["a", "b", "c", "d"]
這樣就可以按照數字分割字符串了,是不是很強大。看完這兩篇博客基本就能對平時用到的JavaScript正則表達式游刃有余了。要求在前端把一個div中的英文段落單詞首字母都換成大寫,你是不是知道該怎麼做了?
司徒正美 JavaScript正則表達式
Regexper