Python re.sub() 替换时如何引用匹配到的分组内容

re.sub()中引用捕获组最常用\1、\2等数字形式,命名组用\g更清晰,函数替换通过Match对象的group()方法获取匹配内容。

re.sub() 中用 \1\2 引用分组最直接

匹配后想在替换字符串里复用捕获内容,最常用方式就是用反斜杠加数字:\1 表示第一个括号内的匹配,\2 是第二个,以此类推。这是正则引擎原生支持的语法,无需额外函数。

注意:数字只认 () 捕获组,不认 (?:...) 非捕获组;且编号从左到右按开括号顺序算,嵌套也一样。

  • re.sub(r"(\w+)@(\w+\.\w+)", r"[\1] at [\2]", "user@example.com")"[user] at [example.com]"
  • 如果写成 r"\0",它代表整个匹配(等价于 re.sub(..., r"\g", ...)),但容易和八进制转义混淆,不建议用
  • 当替换字符串里要字面量输出反斜杠(比如 Windows 路径),得写双反斜杠:r"C:\\temp\\file.txt",否则 \t 会被解释为制表符

\g 按命名组引用更清晰

当正则里有多个括号、逻辑复杂时,靠数 \1 \2 容易错。改用命名捕获组 + \g 语法,可读性和维护性高得多。

  • 定义命名组用 (?P...),比如 r"(?P\d{4})-(?P\d{2})"
  • 替换时写 r"\g/\g",比 r"\1/\2" 直观得多
  • 命名组名必须是合法 Python 标识符(字母/下划线开头,只含字母数字下划线),不能是纯数字如 \g
  • \g 依然表示整个匹配,和 \0 不同,它不会被误解析为字符编码

用函数做替换时,通过 Match 对象取 .group()

当替换逻辑不能靠静态字符串搞定(比如要转大小写、查表、条件判断),就得传一个函数给 re.sub()。函数接收一个 re.Match 对象,从中调用 .group(1).group("name") 等方法取值。

  • 函数签名必须是 def replacer(match: re.Match) -> str:,返回替换后的字符串
  • match.group(0) 是整个匹配,match.group(1) 是第一捕获组,match.group("city") 是命名组
  • 如果某组未匹配(比如用了 ? 修饰且没出现),.group() 会抛 IndexError,要用 .groupdict().get("name", "") 更安全
  • 示例:re.sub(r"(\

    d+)", lambda m: str(int(m.group(1)) * 2), "a1b3c5")
    "a2b6c10"

常见坑:转义、空匹配、Unicode 和性能

看似简单,实际踩坑不少。几个高频问题:

  • 替换字符串里写 "\1"(普通双引号字符串)可能出错——Python 会先尝试把 \1 当 ASCII 控制字符处理。务必用原始字符串 r"\1"
  • 正则匹配空字符串(比如 r"a*")会导致无限循环替换,re.sub() 内部会跳过空匹配,但逻辑仍需警惕
  • 处理中文等 Unicode 文本时,确保正则本身带 re.U 标志(Python 3 默认开启,但显式写上更稳妥),否则 \w 可能不匹配汉字
  • 大量替换时,函数式替换比字符串模板慢一截。如果只是简单拼接,优先用 r"\1...\2";复杂逻辑才上函数

真正难的不是语法,而是写对正则本身——组怎么括、要不要非贪婪、边界怎么锚定。引用只是“最后一公里”,前面没匹配准,后面全白搭。