捕获分组通常用数字编号来标识,但这样有几个问题:
- 数字编号不够直观,虽然规则是“从左到右按照开括号出现的顺序计数”,但括号多了难免混淆
- 引用时不够方便,有可能出现二义性
未解决这类问题,一些语言和工具提供了命名分组(named grouping),可已经它看作另一种捕获分组,但是标识是容易记忆和辨别的名字,而不是数字编号。
命名分组的记法并不复杂。在Python中是?P<name>
来分组的,其中name
是赋予这个分组的名字。使用时再用group(name)
来获得对应分组匹配的文本。
例3-32 命名分组捕获
nameRegex = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})')
result = nameRegex.search('2018-12-17')
print(result.group('year')) # 2018
print(result.group('month')) # 12
print(result.group('day')) # 17
因为数字编号分组的历史更长,为保证向后兼容,即便使用了命名分组,每个分组同时也具有数字编号,其编号规则没有变化。
例3-33 命名分组捕获时仍保留了数字编号
nameRegex = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})')
result = nameRegex.search('2018-12-17')
print(result.group('year'), result.group(1)) # 2018 2018
print(result.group('month'), result.group(2)) # 12 12
print(result.group('day'), result.group(3)) # 17 17
在Python中,如果使用了命名分组,在表达式中反向引用时,必须使用(?P=name)
的记法;而要进行正则表达式替换,则需要写作\g<name>
,其中的name
是分组的名字。
例3-34 命名分组的引用方法
print(re.search(r'(?P<char>[a-z])(?P=char)', 'aa')) # 'aa'
print(re.search(r'([a-z])(\1)', 'aa')) # 'aa'
print(re.sub(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '\g<day>_\g<month>_\g<year>', '2018-12-17')) # 17_12_2018