原文連接: https://www.cnblogs.com/yalong/p/14133482.html
分組概念的由來:
對於要重復單個字符,非常簡單,直接在字符后加上限定符即可,例如 a+ 表示匹配1個或一個以上的a,a?表示匹配0個或1個a, 這些限定符如下所示:
| X ? |
X ,一次或一次也沒有 |
| X * |
X ,零次或多次 |
| X + |
X ,一次或多次 |
| X { n } |
X ,恰好 n 次 |
| X { n ,} |
X ,至少 n 次 |
| X { n , m } |
X ,至少 n 次,但是不超過 m 次
|
但是我們如果要對多個字符進行重復怎么辦呢 此時我們就要用到分組,我們可以使用小括號"()"來指定要重復的子表達式,然后對這個子表達式進行重復,例如:(abc)? 表示0個或1個abc 這里一 個括號的表達式就表示一個分組 。
分組可以分為兩種形式,捕獲組和非捕獲組。
捕獲分組
捕獲性分組工作模式()會把每個分組里匹配的值保存起來, 保存在內存中。
比如利用捕獲性分組把 2010/11/12 轉成 2010-11-12
方法一:通過exec函數
let str = '2010/11/12 let pattern = /(\d+)\/(\d+)\/(\d+)/ let arr = pattern.exec(str) console.log(arr); // ['2010/11/12', '2010', '11', '12'] console.log(arr[0]); //'2010/11/12' 匹配到的字符串 console.log(arr[1]); //'2010' 第一個分組(\d+)的值 console.log(arr[2]); //'11' console.log(arr[3]); //'12' //這時候兩個分組的值都得到了,接下來用字符串拼接法實現互換 let result = `${arr[1]}-${arr[2]}-${arr[3]}` console.log(result) //2010-11-12
方法二:通過屬性$1-9
let str = '2010/11/12' let pattern = /(\d+)\/(\d+)\/(\d+)/ pattern.test(str); //這個地方必須運行正則匹配一次,方式不限,可以是test()、exec()、以及String的正則方式 console.log(RegExp.$1) //'2010' 第一個分組([a-z]+)的值 console.log(RegExp.$2) //'11' console.log(RegExp.$3) //'12' let result = `${RegExp.$1}-${RegExp.$2}-${RegExp.$3}` console.log(result) //2010-11-12
方法三:通過String的replace()
let str = '2010/11/12' let pattern = /(\d+)\/(\d+)\/(\d+)/ let result = str.replace(pattern,"$1-$2-$3"); //這里的$1、$2與方法二里的RegExp.$1、RegExp.$2作用是相同的。 console.log(result) //2010-11-12
非捕獲性分組:(?:)
非捕獲性分組工作模式下分組(?:)會作為匹配校驗,並出現在匹配結果字符里面,但不作為子匹配返回
非捕獲組不會捕獲文本,也不會將它匹配到的內容單獨分組來放到內存中。所以,使用非捕獲組較使用捕獲組更節省內存
比如利用非捕獲性分組獲取字符串000aaa111,而且只返回一個值為aaa111的數組:
//先看用捕獲性分組匹配會返回什么 let str1 = '000aaa111'; let pattern = /([a-z]+)(\d+)/; //捕獲性分組匹配 let arr = pattern.exec(str1); console.log(arr) //['aaa111','aaa','111'] 結果子串也獲取到了,這並不是我們想要的結果 //非捕獲性分組 let str2 = '000aaa111'; let pattern2 = /(?:[a-z]+)(?:\d+)/; //非捕獲性分組匹配 let arr2 = pattern2.exec(str2); console.log(arr2) //['aaa111'] 結果正確
斷言分為先行斷言(前瞻),后發斷言(后顧)
前瞻 = 先行斷言
(?=) 正向前瞻 = 正向零寬先行斷言
(?!) 反向前瞻 = 負向前瞻 = 負向零寬先行斷言
后顧 = 后發斷言
(?<=) 正向后顧 = 正向零寬后發斷言
(?<!) 反向后顧 = 負向后顧 = 負向零寬后發斷言
詳細解釋如下表所示:
| (?=X ) |
零寬度正先行斷言。僅當子表達式 X 在 此位置的右側匹配時才繼續匹配。例如,/w+(?=/d) 與后跟數字的單詞匹配,而不與該數字匹配。此構造不會回溯。 |
| (?!X) |
零寬度負先行斷言。僅當子表達式 X 不在 此位置的右側匹配時才繼續匹配。例如,例如,/w+(?!/d) 與后不跟數字的單詞匹配,而不與該數字匹配 。 |
| (?<=X) |
零寬度正后發斷言。僅當子表達式 X 在 此位置的左側匹配時才繼續匹配。例如,(?<=19)99 與跟在 19 后面的 99 的實例匹配。此構造不會回溯。 |
| (?<!X) |
零寬度負后發斷言。僅當子表達式 X 不在此位置的左側匹配時才繼續匹配。例如,(?<!19)99 與不跟在 19 后面的 99 的實例匹配 |
簡單來說,前瞻就是 看后面等於(?=), 后面不等於 (?!), 后顧就是 前面等於 (?<=), 后面不等於 (?<!)
比如 (?<!AA)eat(?=milk) 表示, eat 前面不能是AA, 后面必須是 milk
比如 (?<=AA)eat(?!milk) 表示 eat 前面必須是AA, 后面不能是milk
示例1 匹配字符串:
let str1 = "VVeatmilk" let str2 = "AAeatfood" let patt1 = new RegExp("(?<!AA)eat(?=milk)"); let patt2 = new RegExp("(?<=AA)eat(?!milk)"); let result1 = patt1.test(str1); let result2 = patt2.test(str2); console.log(result1) // true console.log(result2) // true
示例2 匹配div標簽:
let str = "<div>我是div</div>" let patt = str.match('(?<=<div>).*(?=</div>)') console.log(patt) // 我是div
匹配div標簽 也可用 下面這種方式實現
let str = "<div>我是div</div>" let patt = str.match('<div>(.*?)</div>') console.log(patt) // 我是div
