Python · 5 分钟阅读
`re`:正则表达式
目录
- 1. 导入
- 2. 四个最常用函数
- 3.
compile:重复使用同一个模式 - 4. 常用语法
- 5.
re.sub的回调替换 - 6. flags
- 7. 实战:从一段文本里抽取所有邮箱
- 8. 常见误区
- 9. 第三方补充
- 10. 小结
正则表达式(Regular Expression)是字符串模式匹配的强大工具。Python 的
re 模块把它完整带到了标准库,并提供匹配、搜索、替换、分割等核心能力。
能用
str方法就别用正则(如s.startswith/s.endswith/
s.split);正则可读性差、维护成本高。能用结构化解析就别用正则
(HTML 用lxml、JSON 用json、配置文件用pyyaml/tomllib)。
1. 导入
import re
2. 四个最常用函数
| 函数 | 作用 |
|---|---|
re.search(pattern, s) |
找第一个匹配,返回 Match 或 None |
re.match(pattern, s) |
从开头匹配 |
re.findall(pattern, s) |
返回所有匹配(列表) |
re.finditer(pattern, s) |
返回所有匹配(迭代器) |
re.sub(pattern, repl, s) |
替换 |
re.split(pattern, s) |
按模式分割 |
import re
m = re.search(r"cat", "The cat is on the roof.")
if m:
print(m.group()) # 'cat'
print(m.start(), m.end()) # 4 7
建议给模式加
r"..."前缀(原始字符串),避免\转义噩梦。
3. compile:重复使用同一个模式
word3 = re.compile(r"\b\w{3}\b")
print(word3.findall("The cat is on the roof."))
# ['The', 'cat', 'the']
re.compile() 后还能直接调用方法(p.search(s)、p.findall(s)),省去
每次解析模式的开销,写库 / 高频场景强烈建议。
4. 常用语法
4.1 字符匹配
| 模式 | 含义 |
|---|---|
. |
任意字符(除换行) |
\d / \D |
数字 / 非数字 |
\w / \W |
单词字符 [A-Za-z0-9_] / 非单词字符 |
\s / \S |
空白 / 非空白 |
[abc] |
字符类 |
[^abc] |
排除 |
[a-z] |
范围 |
4.2 边界
| 模式 | 含义 |
|---|---|
^ |
字符串开头 |
$ |
字符串结尾 |
\b |
单词边界 |
\B |
非单词边界 |
4.3 量词
| 模式 | 含义 |
|---|---|
* |
0 次或多次 |
+ |
1 次或多次 |
? |
0 次或 1 次 |
{n} |
恰好 n 次 |
{n,} |
至少 n 次 |
{n,m} |
n ~ m 次 |
*? / +? / ?? |
非贪婪(尽量少匹配) |
4.4 分组与引用
# () 分组,(?P<name>...) 命名分组
m = re.match(r"(?P<year>\d{4})-(?P<month>\d{2})", "2026-08")
m.group("year") # '2026'
m.group(2) # '08'(按编号从 1 开始)
m.groupdict() # {'year': '2026', 'month': '08'}
反向引用:替换时用 \1 / \g<name>:
re.sub(r"(\w+) \1", r"\1", "hello hello world") # 'hello world'
4.5 断言(不占字符)
| 模式 | 含义 |
|---|---|
(?=...) |
正向先行:后面是 … |
(?!...) |
负向先行:后面不是 … |
(?<=...) |
正向后顾:前面是 … |
(?<!...) |
负向后顾:前面不是 … |
# 提取以 .py 结尾的单词(不包含 .py)
re.findall(r"\b\w+(?=\.py\b)", "main.py lib.py data.json")
# ['main', 'lib']
5. re.sub 的回调替换
def double(m):
return str(int(m.group()) * 2)
re.sub(r"\d+", double, "a1 b22 c333")
# 'a2 b44 c666'
比 f-string 写死的复杂替换更灵活。
6. flags
| 标志 | 含义 |
|---|---|
re.IGNORECASE / re.I |
忽略大小写 |
re.MULTILINE / re.M |
^ / $ 匹配每行 |
re.DOTALL / re.S |
. 匹配换行 |
re.VERBOSE / re.X |
模式里可以写注释 / 空白 |
re.ASCII / re.A |
\w 等只匹配 ASCII |
re.search(r"^hello", "Hello world", re.IGNORECASE) # 匹配
pattern = re.compile(r"""
(?P<area>\d{3})-
(?P<first>\d{4})
""", re.VERBOSE)
7. 实战:从一段文本里抽取所有邮箱
import re
text = """
请联系 support@example.com 或 sales@foo-bar.co.uk;
无效的如 hello@, @world.com 不会被匹配。
"""
EMAIL = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
print(EMAIL.findall(text))
# ['support@example.com', 'sales@foo-bar.co.uk']
“真要 100% 准确请用 email-validator 库”;正则匹配只是“看起来像”。
8. 常见误区
re.match只匹配开头:要全文搜索请用re.search。- 贪婪 vs 非贪婪:
.*会尽量长,匹配错了就改用.*?。 - 转义混乱:永远是
r"..."原始字符串写模式。 - 性能:高频匹配一定要
re.compile;re.findall返回列表,大文本用
finditer流式处理。 - 复杂模式拆分:把长正则拆成几行 +
re.VERBOSE,可读性会好很多。 - 不要用正则解析 HTML:标签会嵌套,正则很容易写出 bug。用
lxml/BeautifulSoup。
9. 第三方补充
regex:第三方正则库,支持更多特性(递归、模糊匹配等)。pyparsing:真正需要写“语法”时用它。lark:现代的解析器组合子库。
10. 小结
re是 Python 标准库的正则;绝大多数字符串匹配问题都能解决。- 高频匹配 先 compile;长模式用
re.VERBOSE拆开。 - 分组、命名、断言、回调替换是四个“提升可读性”的关键武器。
- 能用字符串方法 / 库就 别 用正则,别 拿正则解析 HTML。