1. 程式人生 > >JavaScript(E5,6) 正則學習總結學習,可看可不看!

JavaScript(E5,6) 正則學習總結學習,可看可不看!

1.概述

正則表示式(例項)是一種表達文字模式(即字串結構)的方法。

建立方式有兩種方式:

一種是使用字面量,以斜槓表示開始和結束。


var regex = /xyz/

另一種是使用RegExp建構函式。


var regex = new RegExp('xyz'); 

它們的主要區別是,第一種方法在引擎編譯程式碼時,就會新建正則表示式,第二種方法在執行時新建正則表示式,所以前者的效率較高。而且,前者比較便利和直觀,所以實際應用中,基本上都採用字面量定義正則表示式。

2.例項屬性

  1. i:忽略大小寫
  2. m:多行模式
  3. 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方法的返回陣列還包含以下兩個屬性:

  1. input:整個原字串。
  2. 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

參考連結

一個笨笨的碼農,我的世界只能終身學習!

2044692672-5b9f0bed758a8_articlex