Chapter 07 / 10

Python re 模块完全指南

掌握 Python 正则表达式的完整 API:编译、匹配、查找、替换、分割

模块概览

import re

# 主要函数
re.match()      # 从字符串开头匹配
re.search()     # 在整个字符串中搜索
re.findall()    # 找到所有匹配,返回列表
re.finditer()   # 找到所有匹配,返回迭代器
re.sub()        # 替换匹配内容
re.subn()       # 替换并返回替换次数
re.split()      # 按模式分割字符串
re.compile()    # 预编译正则,提升重复使用性能
re.fullmatch()  # 要求整个字符串完全匹配

match vs search vs fullmatch

import re

s = 'hello world'

re.match(r'world', s)      # None(match 从开头开始)
re.search(r'world', s)     # Match(search 搜索整个字符串)
re.fullmatch(r'hello.*', s) # Match(整个字符串必须匹配)

# 实用建议:搜索用 search,验证格式用 fullmatch

Match 对象的方法

m = re.search(r'(\d+)-(\w+)', '编号 42-alpha 序列')
if m:
    m.group()    # '42-alpha'  整个匹配
    m.group(0)   # '42-alpha'  同上
    m.group(1)   # '42'        第1捕获组
    m.group(2)   # 'alpha'     第2捕获组
    m.groups()   # ('42', 'alpha')
    m.start()    # 3   匹配开始位置
    m.end()      # 11  匹配结束位置
    m.span()     # (3, 11)
    m.string     # '编号 42-alpha 序列'  原始字符串

findall 与 finditer

# findall 返回列表
re.findall(r'\d+', 'a1 b22 c333')
# → ['1', '22', '333']

# 有捕获组时,返回捕获组内容的列表
re.findall(r'(\w+)=(\d+)', 'x=1 y=22')
# → [('x', '1'), ('y', '22')]

# finditer 返回 Match 对象迭代器(内存友好)
for m in re.finditer(r'\d+', 'a1 b22 c333'):
    print(m.group(), m.start())

sub 替换

# 简单替换
re.sub(r'\s+', ' ', 'hello   world')   # → 'hello world'

# 用捕获组重组
re.sub(r'(\w+)\s+(\w+)', r'\2 \1', 'hello world')
# → 'world hello'

# 限制替换次数
re.sub(r'\s', '-', 'a b c d', count=2)
# → 'a-b-c d'

# 用函数作为替换参数(动态替换)
def upper_match(m):
    return m.group().upper()

re.sub(r'\b\w{4}\b', upper_match, 'this is a test case')
# → 'THIS is a TEST CASE'

split 分割

# 按空白分割
re.split(r'\s+', '  hello   world  ')
# → ['', 'hello', 'world', '']

# 按多种分隔符分割(包含捕获组时,分隔符也出现在结果中)
re.split(r'[,;]', 'a,b;c,d')
# → ['a', 'b', 'c', 'd']

# 限制分割次数
re.split(r',', 'a,b,c,d', maxsplit=2)
# → ['a', 'b', 'c,d']

编译与重用

# 预编译正则——在循环中大量使用时显著提升性能
pattern = re.compile(r'\d{4}-\d{2}-\d{2}', re.IGNORECASE)

dates = ['2026-03-26', 'invalid', '2025-12-31']
for d in dates:
    m = pattern.fullmatch(d)
    if m:
        print(f'有效日期: {d}')
INFOPython 的 re 模块内部有一个编译缓存(默认 512 个),所以直接调用 re.search() 也会自动缓存编译结果。但对于需要重复调用的模式,显式 compile() 仍是最佳实践——既能提升性能,又让代码意图更清晰。

常见陷阱

# 陷阱1:match 只从开头匹配
re.match(r'\d+', 'abc123')   # None!不是 '123'
re.search(r'\d+', 'abc123')  # Match('123')

# 陷阱2:贪婪导致过度匹配
re.findall(r'<.+>', '<a>text</a>')   # ['<a>text</a>']
re.findall(r'<.+?>', '<a>text</a>')  # ['<a>', '</a>']

# 陷阱3:忘记使用原始字符串
re.search("\d+", text)    # 偶尔可能出现双重转义问题
re.search(r"\d+", text)   # ✅ 始终使用 r"..."

# 陷阱4:findall 有捕获组时行为不同
re.findall(r'\d+', 'a1 b2')          # ['1', '2']
re.findall(r'(\d)+', 'a12 b34')       # ['2', '4'](最后一个捕获)
re.findall(r'(\d+)', 'a12 b34')       # ['12', '34'](正确)

小结