简介
在处理文本数据时,正则表达式(Regular Expressions)是 Python 中一个极其强大的工具。而 捕获分组(Capturing Groups) 则是正则表达式中用于提取特定信息的核心机制之一。本文将深入讲解 Python 中如何使用捕获分组,并通过示例帮助你掌握其用法。
什么是捕获分组?
捕获分组是通过圆括号 () 将正则表达式的一部分"包裹"起来,从而 将匹配到的子字符串单独提取出来 。每个括号定义一个分组,Python 的 re 模块会按顺序为这些分组编号(从 1 开始),并允许你在匹配结果中访问它们。
基本语法
python
import re
pattern = r'(\d{4})-(\d{2})-(\d{2})' # 匹配 YYYY-MM-DD 格式的日期
text = '今天是 2026-01-14'
match = re.search(pattern, text)
if match:
print("完整匹配:", match.group(0)) # 整个匹配内容
print("年份:", match.group(1)) # 第1个分组:年
print("月份:", match.group(2)) # 第2个分组:月
print("日期:", match.group(3)) # 第3个分组:日
输出:
output
完整匹配: 2026-01-14
年份: 2026
月份: 01
日期: 14
注意 :
group(0)表示整个匹配的字符串,而group(1)、group(2)等表示各个捕获分组的内容。
多个匹配与 findall
当你使用 re.findall() 时,行为会根据是否存在捕获分组而有所不同:
- 没有分组:返回所有完整匹配的字符串列表。
- 有分组 :返回每个匹配中分组内容组成的元组列表(如果只有一个分组,则返回字符串列表)。
python
text = "日期:2025-12-01 和 2026-01-14"
# 无分组
print(re.findall(r'\d{4}-\d{2}-\d{2}', text))
# 输出: ['2025-12-01', '2026-01-14']
# 有分组
print(re.findall(r'(\d{4})-(\d{2})-(\d{2})', text))
# 输出: [('2025', '12', '01'), ('2026', '01', '14')]
命名捕获分组(Named Groups)
除了按位置编号,还可以为分组 命名,使代码更具可读性:
python
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
match = re.search(pattern, '2026-01-14')
if match:
print("年份:", match.group('year'))
print("月份:", match.group('month'))
print("日期:", match.group('day'))
# 也可以通过 groupdict() 获取所有命名分组的字典
print("全部字段:", match.groupdict())
输出:
output
年份: 2026
月份: 01
日期: 14
全部字段: {'year': '2026', 'month': '01', 'day': '14'}
命名分组使用
(?P<name>...)语法,其中name是你自定义的标识符。
非捕获分组(Non-capturing Groups)
有时你只想对正则表达式进行逻辑分组,但 不希望 该部分被单独提取出来。这时可以使用非捕获分组 (?:...):
python
pattern = r'(?:https?://)?(www\.\w+\.\w+)'
text = '访问 https://www.example.com 或 www.test.org'
matches = re.findall(pattern, text)
print(matches) # ['www.example.com', 'www.test.org']
在这个例子中,
(?:https?://)?不会被捕获,只有后面的域名部分被捕获。
实战示例:解析日志行
假设你有一行 Web 服务器日志:
log
127.0.0.1 - - [14/Jan/2026:09:34:00 +0800] "GET /index.html HTTP/1.1" 200 1234
你可以用捕获分组提取关键信息:
python
log_line = '127.0.0.1 - - [14/Jan/2026:09:34:00 +0800] "GET /index.html HTTP/1.1" 200 1234'
pattern = r'''
(?P\d+\.\d+\.\d+\.\d+) # IP 地址
.*?
$$(?P[^$$]+)$$ # 时间戳
.*?
"(?P\w+)\s+(?P[^"]+)" # 请求方法和路径
.*?
(?P\d{3}) # 状态码
'''
match = re.search(pattern, log_line, re.VERBOSE)
if match:
info = match.groupdict()
print(info)
输出(格式化后):
python
{
'ip': '127.0.0.1',
'timestamp': '14/Jan/2026:09:34:00 +0800',
'method': 'GET',
'path': '/index.html HTTP/1.1',
'status': '200'
}
使用
re.VERBOSE可以让正则表达式跨多行并添加注释,提高可读性。
小结
- 捕获分组
(...)用于提取子匹配内容。 - 使用
.group(n)或.group('name')访问分组。 findall()在有分组时返回分组内容而非完整匹配。- 命名分组
(?P<name>...)提升代码可读性。 - 非捕获分组
(?:...)用于逻辑分组但不提取。
📌 提示:正则表达式虽强大,但过度复杂可能降低可维护性。对于结构化数据(如 JSON、XML),优先考虑专用解析器。
自此,本文分享到此结束!!!