js正則高級用法: 分組和斷言


原文連接: 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM