深刻學習正則表達式

正則裏括號的用法

1. 分組

分組:正則表達式裏括號的表達式爲另一組匹配規則正則表達式

捕獲括號:被匹配的子字符串能夠在結果數組的元素 [1]-[n] 中找到,或在被定義的 RegExp 對象的屬性 $1-$9 中找到。數組

代碼舉例:bash

let reg = /\d+(\D+)/
reg.exec('123456abcd')
// ["123456abcd", "abcd", index: 0, input: "123456abcd", groups: undefined]
console.log(RegExp.$1)
// "abcd"
複製代碼

在這個正則表達式裏咱們括號指望的是一組非數字的匹配項,而且執行匹配後可在執行結果的[1]或者RegExp.$1獲得匹配值。工具

正則表達式括號的分組在實際開發中對於咱們解決問題有很是大的用處,例如String.replace()這個方法學習

代碼舉例:開發工具

let str = '123abc'
let reg = /(\d+)(\D+)/
let newStr = str.replace(reg, '$2$1')
console.log(newStr) // abc123
str.replace(reg, function(word, $1, $2){
	console.log(word,$1,$2)
	// word表明字符串在正則匹配到的值, $1表明第一個括號的匹配項, $2表明第二個括號的匹配項
	// 123abc, 123, abc
})
複製代碼

使用正則表達式應用於字符串處理,在上面的例子裏咱們很容易得就把數字和字母的匹配項互換位置。ui

在另一種狀況下若是不想要捕獲這個匹配項,可是又須要加括號匹配條件,咱們可使用非捕獲括號spa

非捕獲括號:匹配項不可以從結果數組的元素 [1]-[n] 或已被定義的 RegExp 對象的屬性 $1-$9 再次訪問到。code

代碼舉例:對象

let reg = /\d+(?:\D+)/
reg.exec('123456abcd')
// ["123456abcd", index: 0, input: "123456abcd", groups: undefined]
複製代碼

在例子裏執行匹配後括號裏的匹配項不會再出現結果裏。

2.反向引用

反向引用:一個反向引用(back reference),指向正則表達式中第 n 個括號(從左開始數)中匹配的子字符串。

代碼舉例:

reg = /(\d+)\D+\1/
reg.exec('123abc123')
// ["123abc123", "123", index: 0, input: "123abc123", groups: undefined]
複製代碼

在正則表達式裏\1表明的是\d+,當咱們在表達式裏有須要重複的時候能夠用這種寫法。

3.零寬斷言

零寬斷言:指一個用來描述或者匹配一系列符合某個句法規則的字符串的單個字符串。

  1. (?=pattern) 正向先行斷言:表明字符串中的一個位置,緊接該位置以後的字符序列可以匹配pattern。
  2. (?!pattern) 負向先行斷言:表明字符串中的一個位置,緊接該位置以後的字符序列不能匹配pattern。
  3. (?<=pattern) 正向後行斷言:表明字符串中的一個位置,緊接該位置以前的字符序列可以匹配pattern。
  4. (?<!pattern) 負向後行斷言:表明字符串中的一個位置,緊接該位置以前的字符序列不能匹配pattern。

正則表達式的括號有時候用來表達斷言,具體的細節咱們在下面問內容詳細說。

貪婪模式與非貪婪模式

貪婪模式與非貪婪模式也是正則裏面比較常見的問題了,平時也會常常應用於開發中解決問題。理解貪婪模式和非貪婪模式對咱們理解正則引擎執行匹配很是有幫助。

貪婪模式

貪婪模式會匹配儘量多的字符,貪婪模式用於匹配優先量詞修飾的子表達式,匹配優先量詞包括:「{m,n}」、「{m,}」、「?」、「*」和「+」

代碼舉例:

let reg = /\d*/
reg.exec('1234567890')
["1234567890", index: 0, input: "1234567890", groups: undefined]
複製代碼

*號表明匹配任意次數,用大括號表明即{0,},在貪婪模式下儘量多的匹配,在例子中由於整個字符串徹底匹配,因此匹配值爲 1234567890。

非貪婪模式

非貪婪模式會匹配儘量少的字符,在匹配量詞後面加上問號就可觸發非貪婪模式:「{m,n}?」、「{m,}?」、「??」、「*?」和「+?」

代碼舉例:

let reg = /\d*?/
reg.exec('1234567890')
// ["", index: 0, input: "1234567890", groups: undefined]
複製代碼

*號表明匹配任意次數,用大括號表明即{0,},由於*號可表明匹配0次,在非貪婪模式下儘量少的匹配,因此在這個例子裏匹配項爲空,即不匹配任何字符串。

零寬斷言

正則表達式的斷言功能很是強大,學習正則的斷言應用,對於解決咱們開發中的問題提供了新的思路。

在理解斷言的執行過程可能會稍微有點繞,可是做爲一個開發確定要有一顆愛折騰的心,哈哈。

下面將只使用正向先行斷言來講明斷言的執行,其餘的三個模式也是大同小異。

先看一個簡單例子:

let reg = /abc(?=123)/
reg.exec('abc123')
// ["abc", index: 0, input: "abc123", groups: undefined]

let reg2 = /abc(?=1234)/
reg2.exec('abc123')
// null

let reg3 = /abc(?=12)/
reg3.exec('abc123')
// ["abc", index: 0, input: "abc123", groups: undefined]
複製代碼

先按照正則的字面意思理解,/abc(?=123)/指望的匹配爲即匹配abc,且abc後面的字符串可以知足括號的匹配規則,注意的是括號裏面能夠爲其餘正則表達式,並非說abc後面只能包含123,而是後面能夠知足括號的匹配則爲斷言成功。

在reg2的匹配過程當中,由於abc後面的字符串不知足括號的匹配規則,因此斷言失敗,執行匹配也失敗了。

在這幾個例子裏尚未體現出咱們概念裏說的意思,重溫一下正向先行斷言的概念

(?=pattern) 正向先行斷言:表明字符串中的一個位置,緊接該位置以後的字符序列可以匹配pattern

概念裏說的意思斷言是在字符串中尋找符合斷言的一個位置

舉例說明:

let reg = /(?=abc).*/
reg.exec('123abc123')
// ["abc123", index: 3, input: "123abc123", groups: undefined]
複製代碼

先分析正則表達式,在知足abc匹配條件的位置後面匹配任意字符。在這個例子裏,存在abc知足斷言的匹配規則,可是爲何匹配到的是abc123?

在這裏就回到咱們的標題,零寬斷言,零寬的意思就是執行斷言是不會消耗咱們正則表達式在匹配過程當中的字符串,而且,斷言是在幫咱們肯定符合斷言匹配規則的位置。因此,(?=abc)會幫咱們肯定一個斷言成功的位置,即3和a之間的位置,而後在這個斷言成功的位置開始執行匹配(.*)。

let reg = /(?=abc)\d+/
reg.exec('123abc123')
// null
複製代碼

在上面的例子中,雖然abc的斷言成功,可是斷言只是幫咱們肯定一個位置,而後再執行\d+匹配規則,由於斷言是不會消耗字符串,因此實際上以abc123去和\d+匹配,最後匹配結果爲null。

基於此咱們可使用斷言幫咱們從一開始檢索整個字符串是否知足某些規則,有助於提高匹配效率。

以下例子,咱們可使用斷言從一開始判斷整個字符串是否所有由數字組成,若是斷言失敗,則不執行匹配,這對於咱們應用於表單校驗很是有助於提高效率。

reg = /(?=^\d+$)\d+/
reg.exec('123456')  // 123456
reg.exec('123456a') // null
複製代碼

另外沒有介紹到的三種模式也是大同小異,在這裏也就不重複贅述。可是兩種後行斷言可能會存在兼容性問題,後行斷言應該是ES2018新增的規範。

零寬斷言的重點是要理解「零寬」以及「位置」這兩個點。

最後總結一下:正則表達式是一門很是實用的工具語言,基本上只要學習了就可以對於咱們實際開發中產生幫助,平時某些開發工具中也可使用正則表達式去檢索某些文檔,對於提高效率真的是幫助很是大。

相關文章
相關標籤/搜索