跳到主要内容

Python 正则表达式实战

正则表达式用于描述文本模式。它适合处理边界清晰的词法任务,比如提取标识符或校验特定格式;但面对嵌套结构或完整语言语法时,请改用解析器。

编写模式时务必使用原始字符串(raw string),避免 Python 字符串转义干扰正则转义:

import re

event = re.compile(
r"^(?P<level>INFO|WARN|ERROR)\s+user=(?P<user>[\w.-]+)$"
)

line = "INFO user=ada"
match = event.fullmatch(line)
if match:
level = match["level"]
user = match["user"]

选择匹配策略

  • search:在字符串任意位置查找第一个匹配。
  • match:仅从字符串开头开始匹配。
  • fullmatch:要求整个输入完全匹配,用于校验时最清晰。
  • finditer:流式返回非重叠的匹配对象;findall 返回列表,但返回结构会随捕获组变化,需谨慎使用。
  • sub:替换匹配项。若替换逻辑依赖上下文,可传入可调用对象(callable),避免二次解析。

对于具有业务含义的字段,使用命名组((?P<name>...));若分组仅用于结构逻辑,使用非捕获组 (?:...)

语义与转义

str 模式下,\w\d 等字符类默认遵循 Unicode 语义。仅当格式严格限定为 ASCII 时,才添加 re.ASCII 标志。注意,忽略大小写匹配不等于基于区域设置(locale)的自然语言比较。

处理不可信文本且需按字面匹配时,必须转义:

user_supplied_prefix = "a.b"
literal_pattern = re.compile(re.escape(user_supplied_prefix))

切勿将任意文本直接插入模式或替换模板。模式转义与替换转义的规则不同,混用会导致意外行为。

性能与校验边界

模糊的嵌套重复和重叠备选分支可能在恶意输入下引发极端回溯(ReDoS)。保持模式简单、限制输入长度、测试边界用例。若有严格超时要求,需隔离正则逻辑或替换实现。标准 re 模块不提供通用的单次匹配超时机制。

正则只能校验语法,无法验证所有权、可达性、权限或业务有效性。对于 URL、邮箱、日期及结构化数据,优先使用标准解析器,再结合业务逻辑校验。

当模式复杂到难以维护时,启用 re.VERBOSE 并添加注释。若复杂度继续上升,引入专用解析器往往是更诚实、更稳健的抽象。

读懂并测试模式

示例中,| 选择日志级别,\s+ 要求至少一个空白字符,[\w.-]+ 接受至少一个单词字符、点或连字符。命名组负责提取字段。使用 fullmatch 时,两端的锚点可以省略。

assert event.fullmatch("INFO user=ada").groupdict() == {"level": "INFO", "user": "ada"}
assert event.fullmatch("DEBUG user=ada") is None
assert event.fullmatch("INFO user=") is None
assert event.fullmatch("INFO\nuser=ada") is not None

最后一项能匹配,因为 \s 包含换行符。如果格式要求一条记录只有一行,字段之间只允许空格和制表符,应改用 [ \t]+。读取文件行后,要按格式要求去掉记录结束符,再执行匹配。

替换文本需要原样插入时,可以用 re.sub(pattern, lambda match: replacement, text)。回调返回的字符串不会被解释为反向引用;re.escape 用于模式,不适用于替换模板。

参考

探索关联

这篇笔记还没有文档关联。

同主题的其他笔记 (46)

打开关联网络