分組進階


有的工具或語言支援分組命名,在概念上,要捕捉的分組數量眾多時,以號碼來區別分組並不方便,這時為分組命名,之後就可以使用名稱取用分組。

不過,不同的工具或語言,分組命名時的語法有些差異,以 Expresso 來說,使用 (?<name> …) 來為分組命名,在同一個規則表示式中使用 \k<name>\k'n' 取用分組。

例如先前談到的 (\d\d)\1 是使用號碼取用分組,若想以名稱取用分組,也可以使用 (?<tens>\d\d)\k<tens>,當分組眾多時,適時為分組命名,就不用為了分組計數而煩惱。

分組捕捉

Python 的話,使用 (?P<name>…) 來為分組命名,在同一個規則表示式中使用 (?P=name) 取用分組。

Java 的話,使用 (?<name>…) 來為分組命名,在同一個規則表示式中使用 \k<name> 取用分組。

JavaScript 的話,ECMAScript 2018(ES9)支援分組命名,使用 (?<name>…) 為分組命名,在同一個規則表示式中使用 \k<name> 取用分組。

有些語言或工具支援規則表示式的條件式,像是 Expresso、Python(而 Java、JavaScript 不支援),可使用 (?(id/name)yes-pattern|no-pattern) 來根據先前是否有符合的分組,動態地組成整個規則表示式。

若只使用工具程式,只能輸入規則表示式的情況下,就可能需要這個功能。

例如,希望驗證郵件位址是否被對稱的 <> 包括,或者是完全沒被 <> 包括,在 Expresso 中可以撰寫 ^(?<arrow><)?(\w+@\w+(?:\.\w+)+)(?(arrow)>|$)

分組捕捉

(?(arrow)>|$) 的部份表示,如果有文字符合了命名為 arrow 的分組,也就是 (?<arrow><)? 的部份,那麼會使用 > 來組成規則表示式,否則就使用 $ 來組成規則表示式。

就上例來說,如果有文字符合了命名為 arrow 的分組,規則表示式等同於 ^(?<arrow><)?(\w+@\w+(?:\.\w+)+)>,否則規則表示式等同於 ^(?<arrow><)?(\w+@\w+(?:\.\w+)+)$

如果是程式語言,基本上不使用這個功能,也可以使用程式編寫方式,來實現這個功能,而且比較易懂,以 Python 為例:

import re

def validate(email):
    mailre = r'\w+@\w+(?:\.\w+)+'
    regex = f'<{mailre}>' if email[0] == '<' else f'^{mailre}$'
    return re.findall(regex, email) != []

print(validate('<user@host.com>')) # 顯示 True
print(validate('user@host.com'))   # 顯示 True 
print(validate('<user@host.com'))  # 顯示 False
print(validate('user@host.com>'))  # 顯示 False

這個範例結合了 Python 3.6 的 f-strings,因而運用程式流程來動態組成規則表示式方便許多,程式碼可讀性上也還不錯。