Python 正则表达式入门
AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。
正则用来在文本里「匹配模式」:提取日志里的 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。