分組是用圓括號“()”括起來的正則表達式,匹配出的內容就表示一個分組。分組有一個例外的情況,分組也可以不使用圓括號,而是使用 | 元字符來表示分組,| 的兩側是兩個分組,例如, exp1 | exp2 表示兩個分組,在嚴格意義閃給,不認為由 | 構成的正則表達式是分組。
分組和捕獲在正則表達式中有着密切的聯系,一般情況下,分組即捕獲,都用小括號完成:
- (exp) :分組,並捕獲該分組匹配到的文本
- (?:exp) :分組,但不捕獲該分組匹配到的文本
什么是捕獲呢?使用小括號指定一個子表達式后,子表達式匹配的文本(即匹配的內容)可以在其他子表達式中重復使用。
一,定義分組
定義分組的三種形式:
- (exp) :把括號內的正則作為一個分組,系統自動分配組號,可以通過分組號引用該分組;
- (?P<name>exp) :定義一個命名分組,分組的正則是exp,系統為該分組分配分組號,可以通過分組名或分組號引用該分組;
- (?:exp) :定義一個不捕獲分組,該分組只在當前位置匹配文本,在該分組之后,無法引用該分組,因為該分組沒有分組名,沒有分組號,也不會占用分組編號;
1,分組編號
在正則表達式中,分組編號是自動進行的。當使用圓括號表示分組時,從正則表達式的左邊開始看,看到的第一個左括號 “(” 表示第一個分組,第二個 "(" 表示第二個分組,依次類推,需要注意的是,有一個隱含的全局分組(分組編號是0),就是整個正則表達式。默認情況下,正則表達式為每個分組自動分配一個組號,規則是:組號從1開始,從左向右,組號依次加1(base+1),例如,第一個分組的組號為1,第二個分組的組號為2,以此類推。
2,分組命名
分組不僅有編號,還能為分組設置名稱,在Python中,使用(?P<name>exp)為正則表達式exp設置別名。
3,無捕獲分組
無捕獲分組沒有名稱,也沒有編號,因此,無法引用無捕獲分組,無捕獲分組不會占用分組編號。
二,引用分組
引用分組的目的是對重復出現的文本進行匹配,注意,不是出現重復的模式,而是出現重復的文本。由於分組有編號和名稱,因此,可以通過名稱和編號來引用前面已經出現的分組。
注意,由於正則表達式的解析是有順序的,從正則表達式的開頭向后解析,引用分組的編號和名稱,必須是前面已經存在的;如果在當前位置引用的編號和名稱不存在,那么模式解析就會報錯。
正則表達式中,可以通過分組名或分組號來引用:
- (?P=name):引用名稱為name的分組
- \n:使用分組的編號來引用分組,分組按照正則表達式中出現的順序編號1、2、3、...
1,通過組號引用分組
在正則表達式前面定義一個分組(exp),在表達式的后面,能夠通過組號引用該分組的表達式,引用分組的語法是:\group_number;
例如,定義正則表達式,該正則表達式表示兩個相同的單詞順序出現:
\b(\w+)\b\s+\1\b
在該正則表達式中,只存在一個分組(\w+),組號是1,在該分組的后面,使用\1來引用該分組,將\1替換為分組的子表達式:
注意,該正則表達式並不等價於:\b(\w+)\b\s+(\w+)\b,該表達式表示兩個單詞是相鄰的:
2,通過分組名引用分組
在正則表達式中,可以對分組命名,命名分組的語法是:(?P<name>exp),分組名是name,通過name來引用該分組的格式是:(?p=group_name),通過分組名和組號引用分組,其引用分組的行為是一樣的,例如,定義一個命名分組:\b(?P<word>\w+)\b\s+(?P=word)\b,在該分組的后面中,使用(?P=word)引用該分組,表示文本中出現完全重復的文本。
3,無法引用的分組
(?:exp):使用這種語法定義的分組,不能引用,只能在當前的位置匹配文本,正則表達式不為該分組自動分配組號。
例如,正則表達式:\b(?:\w+)\b\s+\1\b 是錯誤的,因為無捕獲分組不占用組號, 而正則表達式 \b(?:\w+)?(\w+)\b\s+\1\b 是正確的,第二個分組的組號是1,\1引用的是第二個分組。
三,匹配分組的示例
下面使用Python的re模塊來演示如何使用分組。
1,匹配任意分組
>>> out=re.match('[0-9]?\d$|100','08') >>> out <re.Match object; span=(0, 2), match='08'>
2,使用()定義分組
>>> out=re.match('\w{4,20}@(163|qq|126)\.com','test@qq.com') >>> out <re.Match object; span=(0, 11), match='test@qq.com'>
3,為分組命名,並通過別名來引用分組
>>> out=re.match(r"<(?P<name1>\w*)><(?P<name2>h[1-5])>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.baidu.com</h1></html>") >>> out <re.Match object; span=(0, 35), match='<html><h1>www.baidu.com</h1></html>'>
4,捕獲分組和不捕獲分組
對於捕獲分組,findall有一個特性,就是如果結果中有捕獲的分組,則將捕獲的分組組成tuple返回,tuple的元素是每個分組捕獲的文本。
>>> re.findall(r'(\d{3,4}-)?(\d{7,8})','020-82228888\n0357-4227865') [('020-', '82228888'), ('0357-', '4227865')]
對於不捕獲分組,findall直接返回整個匹配的結果:
>>> re.findall(r'(?:\d{3,4}-)?\d{7,8}','020-82228888\n4227865') ['020-82228888', '4227865']
5,Python對分組引用的支持
引用分組在findall和search中是無效的,但是可以使用在sub函數中。
參考文檔: