分組捕捉


可以使用 () 來將規則表示式分組,除了作為子規則表示式之外,還可以搭配量詞使用。

例如想要驗證電子郵件格式,允許的使用者名稱開頭要是大小寫英文字元,之後可搭配數字,規則表示式可以寫為 ^[a-zA-Z]+\d*,因為 @ 後網域名稱可以有數層,必須是大小寫英文字元或數字,規則表示式可以寫為 ([a-zA-Z0-9]+\.)+,其中使用 () 群組了規則表示式,之後的 + 表示這個群組的表示式符合一次或多次,最後要是 com 結尾,整個結合起來的規則表示式就是 ^[a-zA-Z]+\d*@([a-zA-Z0-9]+\.)+com

若有字串符合了被分組的規則表示式,字串會被捕捉(Capture),以便在稍後回頭參考(Back reference),在這之前,必須知道分組計數,如果有個規則表示式 ((A)(B(C))),其中有四個分組,這是遇到的左括號來計數,所以四個分組分別是:

  1. ((A)(B(C)))
  2. (A)
  3. (B(C))
  4. (C)

分組回頭參考時,是在 \ 後加上分組計數,表示參考第幾個分組的比對結果。

例如,\d\d 要求比對兩個數字,(\d\d)\1 的話,表示要輸入四個數字,輸入的前兩個數字與後兩個數字必須相同,例如輸入 1212 會符合,12 因為符合 (\d\d) 而被捕捉至分組 1,\1要求接下來輸入也要是分組 1 的內容,也就是 12;若輸入 1234 則不符合,因為 12 雖然符合 (\d\d) 而被捕捉,然而 \1 要求接下來的輸入也要是 12,然而接下來的數字是 34,因而不符合。

再來看個實用的例子,["'][^"']*["'] 比對單引號或雙引號中 0 或多個字元,但沒有比對兩個都要是單引號或雙引號,(["'])[^"']*\1 則比對出前後引號必須一致。

規則表示式中的 (?…) 代表擴充標記(Extension notation),括號中首個字元必須是?,而這之後的字元(也就是…的部份),進一步決定了規則表示式的組成意義。

如果不需要分組計數,只是想使用 () 來定義某個子規則,可以使用 (?:…) 來表示不捕捉分組。

例如,若只是想比對郵件位址格式,不打算捕捉分組,可以使用 ^[a-zA-Z]+\d*@(?:[a-zA-Z0-9]+\.)+com
在規則表示式複雜之時,善用 (?:…) 來避免不必要的捕捉分組,對於效能也會有很大的改進。

如果想比對出的對象,之後必須跟隨或沒有跟隨著特定文字,可以使用 (?=…)(?!…),分別稱為 lookahead 與 negative lookahead。例如分別比對出來的名稱最後必須有 Lin:

分組捕捉

如果將上圖中的 (?=Lin) 改為 (?!Lin),就會比對出 Monica。

相對地,如果想比對出的對象,前面必須有或沒有著特定文字,可以使用 (?<=…)(?<!…),分別稱為 lookbehind 與 negative lookbehind。

分組捕捉

JavaScript 實際上是在 ECMAScript 2018(ES9)中才規範了 (?<=…)(?<!…),然而,最新版的 Chrome 與 Node.js 已經支援。