共计 1792 个字符,预计需要花费 5 分钟才能阅读完成。
在前一节中,我们学习了正则表达式中的贪婪与非贪婪匹配。今天我们将深入探讨正则表达式的另一个核心概念——分组与引用。通过分组,可以让匹配更加灵活,并能提取匹配结果中的指定部分,是解析文本的重要技能。
一、分组的基本概念
在正则表达式中,使用小括号 () 表示一个分组。分组有两个主要作用:
- 逻辑分组:将多个字符视为一个整体。
- 捕获内容:提取匹配结果中的某个部分。
例如:
import re
text = "2025-10-14"
pattern = r"(\d{4})-(\d{2})-(\d{2})"
match = re.match(pattern, text)
print(match.groups())
输出结果为:
('2025', '10', '14')
解释:
- 第一个括号
(\d{4})捕获年份 - 第二个括号
(\d{2})捕获月份 - 第三个括号
(\d{2})捕获日期
二、通过编号访问分组
每一个分组都有一个编号(从 1 开始)。可以通过 group(n) 方法访问指定的分组内容。
print(match.group(1)) # 输出 2025
print(match.group(2)) # 输出 10
print(match.group(3)) # 输出 14
如果想一次性获取所有分组内容,可以使用 groups()。
三、分组命名
在复杂的正则中,使用编号容易混乱。Python 提供了 命名分组 功能,通过 (?P<name>pattern) 的形式定义:
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
match = re.match(pattern, text)
print(match.group("year"))
print(match.group("month"))
print(match.group("day"))
输出:
2025
10
14
命名分组可以使代码更具可读性,尤其在大型项目中非常实用。
四、反向引用(Backreference)
分组不仅可以捕获内容,还能在正则表达式中 重复引用 之前匹配的内容。
反向引用的语法是 \1、\2 等,对应第 1、第 2 个分组。
例如:匹配重复的单词:
text = "hello hello world"
pattern = r"(\b\w+\b)\s+\1"
print(re.search(pattern, text))
输出结果:
<re.Match object; span=(0, 11), match='hello hello'>
解释:
- 第一个
(\b\w+\b)匹配一个单词; \1引用第一个分组的内容,因此匹配“相同的单词连续出现”的情况。
五、非捕获组(Non-capturing group)
有时我们只需要分组逻辑但不想捕获结果,可以使用 (?:pattern),称为非捕获组。
示例:
text = "cat dog"
pattern = r"(?:cat|dog)"
print(re.findall(pattern, text))
输出:
['cat', 'dog']
与普通分组不同,非捕获组不会出现在 groups() 结果中。
六、嵌套分组与多级引用
分组可以嵌套使用,引用编号按照左括号出现的顺序确定。
text = "<div><p>Hello</p></div>"
pattern = r"(<(\w+)>)\w+</\2>"
print(re.search(pattern, text).groups())
输出:
('<p>', 'p')
解释:
<(\w+)>捕获标签名p</\2>引用第二个分组,即p,保证匹配的开始和结束标签一致。
七、re.sub()中的分组替换
分组还可以结合 re.sub() 用于替换文本中的特定内容。
text = "2025-10-14"
pattern = r"(\d{4})-(\d{2})-(\d{2})"
result = re.sub(pattern, r"\2/\3/\1", text)
print(result)
输出结果:
10/14/2025
这里的 \1、\2、\3 分别引用分组内容,实现了日期格式的重新排列。
八、小结
- 使用
()进行分组可以提取或控制匹配内容; - 分组可以通过编号或命名访问;
\1、\2等是反向引用,能重复匹配相同内容;(?:pattern)用于逻辑分组但不捕获结果;- 分组还能在
re.sub()中用于灵活替换文本。
分组与引用是正则表达式的高级功能之一,熟练掌握后,你将能轻松应对文本提取、数据清洗以及复杂的格式转换问题。