Python 正则表达式入门

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

Python 3.13

正则用来在文本里「匹配模式」:提取日志里的 IP、校验手机号、批量替换格式。这一篇讲够用的正则与 re 模块。

环境准备

re 是标准库,Python 3.13 下验证。调试推荐用在线工具(如 regex101.com)先试写模式。

re 模块的主要函数

import re

text = "订单号 A-1024 已发货,金额 89.50 元"

re.search(r"A-\d+", text)          # 找第一个匹配,返回 Match 或 None
re.match(r"\d+", text)             # 只从开头匹配(这里返回 None)
re.fullmatch(r"\d+", "1024")       # 整串完全匹配
re.findall(r"\d+\.?\d*", text)     # ['1024', '89.50'],所有匹配的字符串
re.finditer(r"\d+", text)          # 迭代器,逐个给 Match 对象
re.split(r"[,,]", text)            # 按模式切分
re.sub(r"\d+", "N", text)          # 替换,返回新字符串
re.subn(r"\d+", "N", text)         # ('...', 2),还返回替换次数

模式字符串一律加 r 前缀(原始字符串),否则 \d 要写成 \\d。

Match 对象

m = re.search(r"金额 (?P<amount>[\d.]+) 元", text)
m.group()             # '金额 89.50 元',整个匹配
m.group("amount")     # '89.50',命名分组
m.group(1)            # '89.50',按序号
m.start(), m.end()    # 位置下标
m.span()              # (start, end)

先判断有没有匹配再用,否则 m.group() 会抛 AttributeError: 'NoneType'。

常用语法

写法 含义
. 任意字符(默认不含换行)
\d / \D 数字 / 非数字
\w / \W 单词字符(字母数字下划线)/ 非
\s / \S 空白 / 非空白
^ / $ 开头 / 结尾
\b 单词边界
[a-z] / [^0-9] 字符集合 / 取反
* / + / ? 0 次以上 / 1 次以上 / 0 或 1 次
{m,n} m 到 n 次
| 或
(...) 分组,可捕获
(?:...) 分组,不捕获
(?P<name>...) 命名分组

示例:

re.findall(r"\b\d{4}-\d{2}-\d{2}\b", log)      # 日期
re.findall(r"\d{1,3}(?:\.\d{1,3}){3}", log)    # IP 地址
re.findall(r"#[0-9a-fA-F]{6}", css)            # 十六进制颜色

贪婪与非贪婪

量词默认贪婪(尽可能多匹配):

re.search(r"<.*>", "<a><b>").group()      # '<a><b>',吃掉了全部
re.search(r"<.*?>", "<a><b>").group()     # '<a>',加 ? 变非贪婪

提取引号里的内容时尤其要注意:

re.search(r'"(.*?)"', 'name="Tom" age="18"').group(1)   # 'Tom'

编译与标志位

反复使用的模式先编译,可读性和性能都更好:

DATE = re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})")

for m in DATE.finditer(log):
    print(m.group("year"))

常用标志:

re.search(r"error", line, re.IGNORECASE)   # 忽略大小写,可写 re.I
re.findall(r"^#.*", text, re.MULTILINE)    # ^ $ 匹配每行首尾
re.search(r"a . b", text, re.DOTALL)       # 让 . 也匹配换行
re.match(r"# comment", line, re.VERBOSE)   # 允许模式里写空白与注释

re.VERBOSE 适合复杂模式:

pattern = re.compile(r"""
    (?P<area>\d{3})     # 区号
    [- ]?               # 可选分隔符
    (?P<num>\d{8})      # 号码
""", re.VERBOSE)

替换的高级用法

sub 的替换串可以用 \1 或 \g<name> 引用分组:

re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", "2026-11-02")   # '02/11/2026'

替换值可以是函数,按匹配内容动态生成:

def upper(m: re.Match[str]) -> str:
    return m.group().upper()

re.sub(r"\berror\b", upper, log)

常见错误与注意事项

1. match 与 search 搞混

re.match 只在开头匹配,re.search 扫全串。多数场景要的是 search 或 fullmatch。

2. 忘记 raw 字符串

"\d" 在普通字符串里会被当成转义,正则拿到的不是想要的东西。写 r"\d"。

3. 特殊字符未转义

用户输入当模式时必须转义:

re.search(re.escape(user_input), text)

4. 用正则解析 HTML / JSON

HTML 不是正则语言,嵌套标签会把模式写崩。用 BeautifulSoup / lxml;JSON 用 json 模块。

5. 灾难性回溯

嵌套量词(如 (a+)+$)在长文本上会指数级变慢,甚至卡死。避免嵌套量词,或限制长度用 {1,50}。

6. 校验用 findall 而不是 fullmatch

校验整个字符串格式(如手机号)要用 re.fullmatch(r"1\d{10}", phone),用 search 会让 “xx13800138000xx” 通过。

小结

  • search 找第一个、findall 找全部、sub 替换、fullmatch 校验整体。
  • 模式前加 r,量词默认贪婪,加 ? 变非贪婪。
  • 重复使用的模式用 re.compile;复杂模式用 re.VERBOSE 加注释。
  • HTML / JSON 不要正则;用户输入作模式要 re.escape。

下一步