JavaScript(E5,6) 正則學習總結學習,可看可不看!
1.概述
正則表示式(例項)是一種表達文字模式(即字串結構)的方法。
建立方式有兩種方式:
一種是使用字面量,以斜槓表示開始和結束。
var regex = /xyz/
另一種是使用RegExp建構函式。
var regex = new RegExp('xyz');
它們的主要區別是,第一種方法在引擎編譯程式碼時,就會新建正則表示式,第二種方法在執行時新建正則表示式,所以前者的效率較高。而且,前者比較便利和直觀,所以實際應用中,基本上都採用字面量定義正則表示式。
2.例項屬性
- i:忽略大小寫
- m:多行模式
- g:全域性搜尋
3.例項方法
3.1 RegExp.prototype.test()
正則例項物件的test方法返回一個布林值,表示當前模式是否能匹配引數字串。
/小智/.test('小智 終身學習執行者') // true
reg.exec(str) 返回匹配結果陣列,不匹配則返回null,每執行一次exec就向後匹配一次
3.2 RegExp.prototype.exec()
3.2.1 reg.exec(str) 返回匹配結果陣列,不匹配則返回null,每執行一次exec就向後匹配一次
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;
r1.exec(s) // ["x"]
r2.exec(s) // null
3.2.1.2如果表示式裡有括號(),稱為組匹配,返回結果中,第一個是整體匹配結果,後面依次是每個括號匹配的結果
var s = '_x_x';
var r = /_(x)/;
r.exec(s) // ["_x", "x"]
exec方法的返回陣列還包含以下兩個屬性:
- input:整個原字串。
- index:整個模式匹配成功的開始位置(從0開始計數)。
var r = /a(b+)a/;
var arr = r.exec('_abbba_aba_');
arr // ["abbba", "bbb"]
arr.index // 1
arr.input // "_abbba_aba_"
3.2.3 如果表示式中有g選項進行全域性搜尋,則可以多次使用 exec,下次的匹配從上次的結果後開始
var reg = /a/g; var str = 'abc_abc_abc' var r1 = reg.exec(str); r1 // ["a"] r1.index // 0 reg.lastIndex // 1 var r2 = reg.exec(str); r2 // ["a"] r2.index // 4 reg.lastIndex // 5 var r3 = reg.exec(str); r3 // ["a"] r3.index // 8 reg.lastIndex // 9 var r4 = reg.exec(str); r4 // null reg.lastIndex // 0
4.字串的例項方法
4.1 str.match(reg),與 reg.exec相似,但是,如果使用g選項,則str.match一次性返回所有結果。
var s = 'abba';
var r = /a/g;
s.match(r) // ["a", "a"]
r.exec(s) // ["a"]
4.2 str.search(reg) ,返回匹配成功的第一個位置,如果沒有任何匹配,則返回-1。
'_x_x'.search(/x/)
// 1
4.3 str.replace(reg,newstr) ;
用第一個引數reg去匹配,用第二個引數newstr 去替換,正則表示式如果不加g修飾符,就替換第一個匹配成功的值,否則替換所有匹配成功的值。
'aaa'.replace('a', 'b') // "baa"
'aaa'.replace(/a/, 'b') // "baa"
'aaa'.replace(/a/g, 'b') // "bbb"
4.4 str.split(reg[,maxLength]) 用匹配的模式切割,第二個引數是限制返回結果的最大數量
5. 匹配規則
5.1 字面量字元和元字元
大部分字元在正則表示式中,就是字面的含義,比如/a/匹配a,/b/匹配b。如果在正則表示式之中,某個字元只表示它字面的含義(就像前面的a和b),那麼它們就叫做“字面量字元”(literal characters)。除了字面量字元以外,還有一部分字元有特殊含義,不代表字面的意思。它們叫做“元字元”(metacharacters),主要有以下幾個。
(1) 點字元(.)點字元(.)匹配除回車(r)、換行(n) 、行分隔符(u2028)和段分隔符(u2029)以外的所有字元。
/c.t/
上面程式碼中,c.t匹配c和t之間包含任意一個字元的情況,只要這三個字元在同一行,比如cat、c2t、c-t等等,但是不匹配coot。
(2)位置字元
- ^ 表示字串的開始位置
- $ 表示字串的結束位置
// test必須出現在開始位置
/^test/.test('test123') // true
// test必須出現在結束位置
/test$/.test('new test') // true
// 從開始位置到結束位置只有test
/^test$/.test('test') // true
/^test$/.test('test test') // false
(3)選擇符(|)
豎線符號(|)在正則表示式中表示“或關係”(OR),即cat|dog表示匹配cat或dog。
/11|22/.test('911') // true
上面程式碼中,正則表示式指定必須匹配11或22。
5.2 轉義符
正則表示式中那些有特殊含義的元字元,如果要匹配它們本身,就需要在它們前面要加上反斜槓。比如要匹配+,就要寫成+。
/1+1/.test('1+1')
// false
/1\+1/.test('1+1')
// true
正則表示式中,需要反斜槓轉義的,一共有12個字元:^、.、[、$、(、)、|、*、+、?、{和。需要特別注意的是,如果使用RegExp方法生成正則物件,轉義需要使用兩個斜槓,因為字串內部會先轉義一次。
(new RegExp('1\+1')).test('1+1')
// false
(new RegExp('1\\+1')).test('1+1')
// true
5.3 字元類
字元類(class)表示有一系列字元可供選擇,只要匹配其中一個就可以了。所有可供選擇的字元都放在方括號內,比如[xyz] 表示x、y、z之中任選一個匹配。
/[abc]/.test('hello world') // false
/[abc]/.test('apple') // true
有兩個字元在字元類中有特殊含義。
(1)脫字元(^)如果方括號內的第一個字元是[^xyz]
表示除了x、y、z之外都可以匹配:
/[^abc]/.test('hello world') // true
/[^abc]/.test('bbc') // false
如果方括號內沒有其他字元,即只有[^],就表示匹配一切字元,其中包括換行符。相比之下,點號作為元字元(.)是不包括換行符的。
var s = 'Please yes\nmake my day!';
s.match(/yes.*day/) // null
s.match(/yes[^]*day/) // [ 'yes\nmake my day']
上面程式碼中,字串s含有一個換行符,點號不包括換行符,所以第一個正則表示式匹配失敗;第二個正則表示式[^]包含一切字元,所以匹配成功。
(2)連字元(-)
某些情況下,對於連續序列的字元,連字元(-)用來提供簡寫形式,表示字元的連續範圍。比如,[abc]可以寫成[a-c],[0123456789]可以寫成[0-9],同理[A-Z]表示26個大寫字母。
/a-z/.test('b') // false
/[a-z]/.test('b') // true
以下都是合法的字元類簡寫形式。
[0-9.,]
[0-9a-fA-F]
[a-zA-Z0-9-]
[1-31]
上面程式碼中最後一個字元類[1-31],不代表1到31,只代表1到3。
另外,不要過分使用連字元,設定一個很大的範圍,否則很可能選中意料之外的字元。最典型的例子就是[A-z],表面上它是選中從大寫的A到小寫的z之間52個字母,但是由於在 ASCII 編碼之中,大寫字母與小寫字母之間還有其他字元,結果就會出現意料之外的結果。
/[A-z]/.test('\\') // true
上面程式碼中,由於反斜槓('')的ASCII碼在大寫字母與小寫字母之間,結果會被選中。
5.4 預定義模式
預定義模式指的是某些常見模式的簡寫方式。
- d 匹配0-9之間的任一數字,相當於
[0-9]
。 - D 匹配所有0-9以外的字元,相當於
[^0-9]
。 - w 匹配任意的字母、數字和下劃線,相當於
[A-Za-z0-9_]
。 - W 除所有字母、數字和下劃線以外的字元,相當於
[^A-Za-z0-9_]
。 - s 匹配空格(包括換行符、製表符、空格符等),相等於
[ \t\r\n\v\f]
。 - S 匹配非空格的字元,相當於
[^ \t\r\n\v\f]
。 - b 匹配詞的邊界。
- B 匹配非詞邊界,即在詞的內部。
// \s 的例子
/\s\w*/.exec('hello world') // [" world"]
// \b 的例子
/\bworld/.test('hello world') // true
/\bworld/.test('hello-world') // true
/\bworld/.test('helloworld') // false
// \B 的例子
/\Bworld/.test('hello-world') // false
/\Bworld/.test('helloworld') // true
通常,正則表示式遇到換行符(n)就會停止匹配。
var html = "<b>Hello</b>\n<i>world!</i>";
/.*/.exec(html)[0]
// "<b>Hello</b>"
上面程式碼中,字串html包含一個換行符,結果點字元(.)不匹配換行符,導致匹配結果可能不符合原意。這時使用s字元類,就能包括換行符。
var html = "<b>Hello</b>\n<i>world!</i>";
/[\S\s]*/.exec(html)[0]
// "<b>Hello</b>\n<i>world!</i>"
上面程式碼中,[Ss]指代一切字元。
5.5 重複類
模式的精確匹配次數,使用大括號({})表示。{n}表示恰好重複n次,{n,}表示至少重複n次,{n,m}表示重複不少於n次,不多於m次。
/lo{2}k/.test('look') // true
/lo{2,5}k/.test('looook') // true
上面程式碼中,第一個模式指定o連續出現2次,第二個模式指定o連續出現2次到5次之間。
5.6 量詞符
*. ?
問號表示某個模式出現0次或1次,等同於{0, 1}
。*. *
星號表示某個模式出現0次或多次,等同於{0,}
。*. +
加號表示某個模式出現1次或多次,等同於{1,}
。
5.7 貪婪模式
上一小節的三個量詞符,預設情況下都是最大可能匹配,即匹配直到下一個字元不滿足匹配規則為止。這被稱為貪婪模式。
var s = 'aaa';
s.match(/a+/) // ["aaa"]
上面程式碼中,模式是/a+/,表示匹配1個a或多個a,那麼到底會匹配幾個a呢?因為預設是貪婪模式,會一直匹配到字元a不出現為止,所以匹配結果是3個a。
如果想將貪婪模式改為非貪婪模式,可以在量詞符後面加一個問號。
var s = 'aaa';
s.match(/a+?/) // ["a"]
除了非貪婪模式的加號,還有非貪婪模式的星號(*)和非貪婪模式的問號(?)
-
+?
:表示某個模式出現1次或多次,匹配時採用非貪婪模式。 -
*?
:表示某個模式出現0次或多次,匹配時採用非貪婪模式。 -
??
:表格某個模式出現0次或1次,匹配時採用非貪婪模式。
5.8 組匹配
(1)概述正則表示式的括號表示分組匹配,括號中的模式可以用來匹配分組的內容。
/fred+/.test('fredd') // true
/(fred)+/.test('fredfred') // true
上面程式碼中,第一個模式沒有括號,結果+只表示重複字母d,第二個模式有括號,結果+就表示匹配fred這個詞。
下面是另外一個分組捕獲的例子。
var m = 'abcabc'.match(/(.)b(.)/);
m
// ['abc', 'a', 'c']
上面程式碼中,正則表示式/(.)b(.)/一共使用兩個括號,第一個括號捕獲a,第二個括號捕獲c。
注意,使用組匹配時,不宜同時使用g修飾符,否則match方法不會捕獲分組的內容。
var m = 'abcabc'.match(/(.)b(.)/g);
m // ['abc', 'abc']
正則表示式內部,還可以用n引用括號匹配的內容,n是從1開始的自然數,表示對應順序的括號。
/(.)b(.)\1b\2/.test("abcabc")
// true
上面的程式碼中,1表示第一個括號匹配的內容(即a),2表示第二個括號匹配的內容(即c)。
(2)非捕獲組(?:x)
稱為非捕獲組(Non-capturing group),表示不返回該組匹配的內容,即匹配的結果中不計入這個括號。
非捕獲組的作用請考慮這樣一個場景,假定需要匹配foo或者foofoo,正則表示式就應該寫成/(foo){1, 2}/,但是這樣會佔用一個組匹配。這時,就可以使用非捕獲組,將正則表示式改為/(?:foo){1, 2}/,它的作用與前一個正則是一樣的,但是不會單獨輸出括號內部的內容。
var m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]
上面程式碼中的模式,一共使用了兩個括號。其中第一個括號是非捕獲組,所以最後返回的結果中沒有第一個括號,只有第二個括號匹配的內容。
(3)先行斷言
x(?=y)稱為先行斷言(Positive look-ahead),x只有在y前面才匹配,y不會被計入返回結果。比如,要匹配後面跟著百分號的數字,可以寫成/d+(?=%)/。
“先行斷言”中,括號裡的部分是不會返回的。
var m = 'abc'.match(/b(?=c)/);
m // ["b"]
上面的程式碼使用了先行斷言,b在c前面所以被匹配,但是括號對應的c不會被返回。
(4)先行否定斷言x(?!y)稱為先行否定斷言(Negative look-ahead),x只有不在y前面才匹配,y不會被計入返回結果。比如,要匹配後面跟的不是百分號的數字,就要寫成/d+(?!%)/。
/\d+(?!\.)/.exec('3.14')
// ["14"]
上面程式碼中,正則表示式指定,只有不在小數點前面的數字才會被匹配,因此返回的結果就是14。
6. 實戰
6.1 消除字串首尾兩端的空格
var str = ' #id div.class ';
str.replace(/^\s+|\s+$/g, '')
// "#id div.class"
6.2 驗證手機號碼
var reg = /1[24578]\d{9}/;
reg.test('154554568997'); //true
reg.test('234554568997'); //false
6.3 把手機號碼替換成 *
var reg = /1[24578]\d{9}/;
var str = '姓名:張三 手機:18210999999 性別:男';
str.replace(reg, '***') //"姓名:張三 手機:*** 性別:男"
6.4 匹配網頁標籤
var strHtlm = '小智小智<div>[email protected]</div>小智小智';
var reg = /<(.+)>.+<\/\1>/;
strHtlm.match(reg); // ["<div>[email protected]</div>"]
6.5 替換敏感字
let str = '中國共產黨中國人民解放軍中華人民共和國';
let r = str.replace(/中國|軍/g, input => {
let t = '';
for (let i = 0; i<input.length; i++) {
t += '*';
}
return t;
})
console.log(r); //**共產黨**人民解放*中華人民共和國
6.6 千位分隔符
let str = '100002003232322';
let r = str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
console.log(r); //100,002,003,232,322
參考連結
一個笨笨的碼農,我的世界只能終身學習!