模式生成与向量格式教程:第 4 章 Python re 模块实战
说到正则表达式,在 Python 里干活,绕不开的就是 re 模块。很多人觉得正则难,其实不是语法难,是不知道用什么函数去匹配。
今天我们就来聊聊 re 模块里的四大金刚:re.match、re.search、re.findall 和 re.sub。我会把每个函数在什么时候用、怎么用,以及我踩过的坑都讲清楚。
1. re.match —— 从开头开始
说白了,re.match 只匹配字符串的开头。它就像个守门员,只在门口检查。
我来举个例子:
import re
text = "Hello World! Hello Python!"
result = re.match(r"Hello", text)
if result:
print(result.group()) # 输出: Hello
result2 = re.match(r"World", text)
print(result2) # 输出: None
为什么会这样?因为 World 不在开头。我在日志分析项目里用过很多次,比如检查一行日志是不是以 "ERROR" 开头。用 re.match 就特别合适。如果你需要匹配中间的内容,别用这个。
re.match 比用 str.startswith() 更灵活,因为它支持正则模式。
2. re.search —— 全局搜索
re.search 就灵活多了。它在整个字符串中搜索,找到第一个匹配就停下来。你想想看,如果要在文本里找电话号码或者邮箱,用这个最合适。
text = "我的邮箱是 alice@example.com,备用邮箱是 bob@test.org"
match = re.search(r"\w+@\w+\.\w+", text)
if match:
print(match.group()) # 输出: alice@example.com
注意,它只返回第一个匹配。要是你想找所有的邮箱,那就得用下一个函数了。
re.search 来提取所有链接,结果只拿到一个。后来才意识到,应该用 re.findall。
3. re.findall —— 全部提取
嗯,这里要注意。re.findall 返回所有非重叠匹配的列表。如果模式里有分组,它返回的是元组列表。
text = "手机号: 13800138000, 13912345678"
numbers = re.findall(r"1[3-9]\d{9}", text)
print(numbers) # 输出: ['13800138000', '13912345678']
# 有分组的情况
text2 = "alice@example.com, bob@test.org"
matches = re.findall(r"(\w+)@(\w+\.\w+)", text2)
print(matches) # 输出: [('alice', 'example.com'), ('bob', 'test.org')]
我个人习惯在处理日志或者爬虫数据时,优先用 re.findall。它帮你一次性提取完所有目标数据,效率很高。
| 函数 | 作用 | 返回值 | 适用场景 |
|---|---|---|---|
re.match |
从开头匹配 | Match 对象或 None | 检查前缀 |
re.search |
查找第一个匹配 | Match 对象或 None | 查找单个目标 |
re.findall |
查找所有匹配 | 列表(或元组列表) | 批量提取 |
re.sub |
替换匹配的文本 | 替换后的字符串 | 文本清理 |
4. re.sub —— 替换与清洗
最后这个,re.sub,是文本处理的大杀器。它做的事情很简单:找到匹配的内容,换成你指定的新内容。
text = "今天是2025年01月01日,明天是01月02日"
new_text = re.sub(r"\d{4}年", "2026年", text)
print(new_text) # 输出: 今天是2026年01月01日,明天是01月02日
# 更复杂的用法:使用回调函数
def replace_date(match):
month = match.group(1)
day = match.group(2)
return f"{day}月{month}日"
text2 = "日期: 05-20, 12-25"
result = re.sub(r"(\d{2})-(\d{2})", replace_date, text2)
print(result) # 输出: 日期: 20月05, 25月12
嗯,有一次我同事没注意替换的边界,结果把不该改的词也改掉了。用 re.sub 时,正则要写得精准,不然容易误伤。
re.sub 还可以指定替换次数,通过 count 参数控制。默认替换所有匹配,改成 count=1 就只替换第一个。
知识体系图谱
下面这张图总结了这四种函数的核心逻辑和关系:
实际项目中的建议
我建议你在开发前,先想清楚目标:是检查开头(match)、找单个目标(search)、提取所有(findall)还是做替换(sub)。选错函数,代码就得不偿失。
- 验证输入格式:用
re.match(如检查手机号开头是否是 1)。 - 提取第一个关键信息:用
re.search(如从一大段文字里找第一个日期)。 - 爬虫或日志解析:用
re.findall批量抓取。 - 数据清洗:用
re.sub替换脏数据或格式化文本。
还有一个容易被忽略的点:预编译正则。如果你要反复使用同一个模式,先 re.compile 一下,能提升不少性能。
# 预编译示例
pattern = re.compile(r"\d{3}-\d{8}")
texts = ["010-12345678", "021-87654321"]
for t in texts:
match = pattern.search(t)
if match:
print(match.group())
好了,这四种函数基本覆盖了日常 90% 的正则需求。记住它们的差异,写代码的时候就不会犹豫了。