可以使用 ()
來將規則表示式分組,除了作為子規則表示式之外,還可以搭配量詞使用。
例如想要驗證電子郵件格式,允許的使用者名稱開頭要是大小寫英文字元,之後可搭配數字,規則表示式可以寫為 ^[a-zA-Z]+\d*
,因為 @
後網域名稱可以有數層,必須是大小寫英文字元或數字,規則表示式可以寫為 ([a-zA-Z0-9]+\.)+
,其中使用 ()
群組了規則表示式,之後的 +
表示這個群組的表示式符合一次或多次,最後要是 com
結尾,整個結合起來的規則表示式就是 ^[a-zA-Z]+\d*@([a-zA-Z0-9]+\.)+com
。
若有字串符合了被分組的規則表示式,字串會被捕捉(Capture),以便在稍後回頭參考(Back reference),在這之前,必須知道分組計數,如果有個規則表示式 ((A)(B(C)))
,其中有四個分組,這是遇到的左括號來計數,所以四個分組分別是:
((A)(B(C)))
(A)
(B(C))
(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 已經支援。