Python3 re 模块
Python 的
1. 混淆
4. 忽略换行符的匹配(
re模块是实现正则匹配、替换、提取、分割的核心工具 —— 小到验证手机号格式、提取日志中的关键信息,大到爬虫数据解析、文本清洗,都离不开它。本文从基础语法到实战场景,拆解re模块的核心用法、进阶技巧与避坑要点,帮你彻底掌握正则表达式在 Python 中的应用。
一、re 模块核心能力与应用场景
re模块封装了正则表达式的所有核心操作,相比手动字符串处理(如split/replace),它能通过 “模式匹配” 实现更灵活、更复杂的文本操作,典型应用场景:- 格式验证:手机号、邮箱、身份证号等合法性校验;
- 数据提取:从日志 / HTML/JSON 文本中提取指定规则的内容;
- 文本清洗:批量替换敏感词、去除冗余字符、统一文本格式;
- 字符串分割:按非固定分隔符(如多个空格、标点)分割文本。
二、re 模块核心函数 / 方法(速查对比)
re模块提供了两类核心接口:直接调用函数(快速使用)、编译后调用方法(高性能复用),核心功能对比如下:| 函数 / 方法 | 核心作用 | 匹配范围 | 返回值类型 | 适用场景 |
|---|---|---|---|---|
re.match() |
从字符串开头匹配 | 仅开头 | 匹配对象(Match)/None | 验证字符串是否以指定规则开头 |
re.search() |
扫描整个字符串找第一个匹配项 | 全字符串 | 匹配对象(Match)/None | 查找字符串中是否存在目标内容 |
re.findall() |
查找所有匹配项 | 全字符串 | 列表(无匹配则空列表) | 批量提取所有符合规则的内容 |
re.finditer() |
查找所有匹配项(迭代器) | 全字符串 | 迭代器(含 Match 对象) | 处理大量匹配结果(节省内存) |
re.sub() |
替换匹配的内容 | 全字符串 | 替换后的新字符串 | 文本清洗、批量替换 |
re.split() |
按匹配规则分割字符串 | 全字符串 | 分割后的列表 | 非固定分隔符的字符串分割 |
re.compile() |
编译正则表达式为 Pattern 对象 | - | Pattern 对象 | 正则表达式需多次复用(性能优化) |
三、基础用法:核心函数实战
1. 匹配基础:match vs search(最易混淆)
re.match():仅匹配字符串开头
import re
# 示例1:匹配开头(成功)
text = "Python3 re模块教程"
result = re.match(r"Python3", text)
print(result.group()) # 输出:Python3(group()获取匹配内容)
# 示例2:非开头匹配(失败)
result = re.match(r"re模块", text)
print(result) # 输出:None
re.search():扫描整个字符串找第一个匹配
# 示例:找第一个数字
text = "Python3 re模块教程,Python2已淘汰"
result = re.search(r"\d", text) # \d匹配数字
print(result.group()) # 输出:3
# 示例:匹配邮箱(第一个)
text = "我的邮箱是test1@xxx.com,备用邮箱test2@yyy.com"
result = re.search(r"\w+@\w+\.\w+", text) # \w匹配字母/数字/下划线
print(result.group()) # 输出:test1@xxx.com
关键区别:match是 “开头校验”,search是 “全局找第一个”,验证格式(如手机号)优先用match,查找内容优先用search。
2. 批量提取:findall vs finditer
re.findall():提取所有匹配项(返回列表)
# 示例1:提取所有数字
text = "价格:99元,折扣:8.5折,库存:120件"
nums = re.findall(r"\d+", text) # \d+匹配1个及以上数字
print(nums) # 输出:['99', '8', '5', '120']
# 示例2:提取所有邮箱
text = "test1@xxx.com, test2@yyy.com, test3@zzz.org"
emails = re.findall(r"\w+@\w+\.\w+", text)
print(emails) # 输出:['test1@xxx.com', 'test2@yyy.com', 'test3@zzz.org']
re.finditer():迭代器返回匹配项(适合大量数据)
# 示例:迭代提取大文本中的所有URL
text = "链接1:https://www.baidu.com,链接2:https://www.google.com"
urls = re.finditer(r"https?://\w+\.\w+", text) # ?表示s可选(匹配http/https)
for url in urls:
print(url.group()) # 依次输出两个URL
3. 文本替换:re.sub ()(支持正则替换)
# 示例1:敏感词替换(替换所有“垃圾”为**)
text = "这是垃圾内容,不要发垃圾信息"
clean_text = re.sub(r"垃圾", "**", text)
print(clean_text) # 输出:这是**内容,不要发**信息
# 示例2:数字脱敏(手机号保留前3后4,中间替换为****)
phone = "13812345678"
mask_phone = re.sub(r"(\d{3})\d{4}(\d{4})", r"\1****\2", phone)
# \1引用第一个分组,\2引用第二个分组
print(mask_phone) # 输出:138****5678
# 示例3:去除所有空格/换行符
text = " 这是一段\n带空格和换行的文本 "
clean_text = re.sub(r"\s+", "", text) # \s匹配所有空白字符(空格、换行、制表符)
print(clean_text) # 输出:这是一段带空格和换行的文本
4. 字符串分割:re.split ()(非固定分隔符)
# 示例1:按多个分隔符分割(逗号/分号/空格)
text = "苹果,香蕉;橙子 葡萄"
fruits = re.split(r"[,; ]", text)
print(fruits) # 输出:['苹果', '香蕉', '橙子', '葡萄']
# 示例2:按任意数量的空格分割
text = "Python re 模块 教程"
words = re.split(r"\s+", text)
print(words) # 输出:['Python', 're', '模块', '教程']
四、进阶技巧:解锁正则的核心能力
1. 分组与引用:精准提取结构化内容
正则的 “分组”(
())是提取结构化数据的核心,通过group(n)或\n引用分组内容:# 示例:提取日志中的时间、级别、内容
log = "2025-12-12 14:30:00 [ERROR] 数据库连接失败"
# 分组1:时间,分组2:级别,分组3:内容
pattern = r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.*)"
result = re.search(pattern, log)
print("时间:", result.group(1)) # 2025-12-12 14:30:00
print("级别:", result.group(2)) # ERROR
print("内容:", result.group(3)) # 数据库连接失败
2. 贪婪 / 非贪婪匹配:控制匹配范围
正则默认是贪婪匹配(尽可能匹配更多字符),加
?可转为非贪婪匹配(尽可能匹配更少字符):# 示例1:贪婪匹配(默认)
text = "<div>标题1</div><div>标题2</div>"
result = re.search(r"<div>.*</div>", text)
print(result.group()) # 输出:<div>标题1</div><div>标题2</div>(匹配全部)
# 示例2:非贪婪匹配(加?)
result = re.search(r"<div>.*?</div>", text)
print(result.group()) # 输出:<div>标题1</div>(仅匹配第一个)
3. 模式修饰符:适配不同匹配场景
通过
flags参数设置匹配模式,常用修饰符:| 修饰符 | 作用 | 示例 |
|---|---|---|
re.I/re.IGNORECASE |
忽略大小写 | re.search(r"python", text, re.I) |
re.M/re.MULTILINE |
多行模式(^/$ 匹配每行开头 / 结尾) | re.findall(r"^error", log, re.M) |
re.S/re.DOTALL |
点(.)匹配换行符 | re.search(r"a.*b", "a\nb", re.S) |
# 示例1:忽略大小写匹配
text = "Python PYTHON python"
result = re.findall(r"python", text, re.I)
print(result) # 输出:['Python', 'PYTHON', 'python']
# 示例2:多行模式匹配每行开头的数字
text = """1. 正则基础
2. 进阶技巧
3. 实战场景"""
result = re.findall(r"^\d", text, re.M)
print(result) # 输出:['1', '2', '3']
4. 转义处理:raw 字符串(r 前缀)的关键作用
正则中的
\是转义符,而 Python 字符串中\也需转义,因此推荐用raw 字符串(r"xxx")避免双重转义:# 错误示例:匹配小数点(未用raw字符串,\d被Python转义)
# result = re.search("^\d+\.\d+$", "123.45") # 实际传递的是^\d+.\d+$,.匹配任意字符
# 正确示例:用raw字符串
result = re.search(r"^\d+\.\d+$", "123.45")
print(result.group()) # 输出:123.45
5. 预编译正则:提升重复匹配性能
若同一正则表达式需多次使用(如循环中匹配),用
re.compile()编译为Pattern对象,可避免重复解析正则,提升性能:# 编译正则(仅需执行一次)
phone_pattern = re.compile(r"^1[3-9]\d{9}$") # 手机号正则
# 批量验证手机号(复用编译后的Pattern)
phones = ["13812345678", "12345678901", "19987654321"]
for phone in phones:
if phone_pattern.match(phone):
print(f"{phone} 是有效手机号")
else:
print(f"{phone} 无效")
五、实战场景:覆盖 80% 的业务需求
场景 1:格式验证(手机号 / 邮箱 / 身份证)
import re
def is_valid_phone(phone):
"""验证手机号(11位,以13/14/15/16/17/18/19开头)"""
pattern = r"^1[3-9]\d{9}$"
return bool(re.match(pattern, phone))
def is_valid_email(email):
"""验证邮箱(简单版,适配多数场景)"""
pattern = r"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$"
return bool(re.match(pattern, email))
def is_valid_idcard(idcard):
"""验证18位身份证号(仅格式,不校验校验位)"""
pattern = r"^[1-9]\d{5}(19|20)\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([1-2]\d)|(3[0-1]))\d{3}[\dXx]$"
return bool(re.match(pattern, idcard))
# 测试
print(is_valid_phone("13812345678")) # True
print(is_valid_email("test_123@xxx.com")) # True
print(is_valid_idcard("110101199001011234")) # True
场景 2:提取 HTML 中的链接和标题
import re
html = """
<a href="https://www.baidu.com">百度</a>
<a href="https://www.github.com">GitHub</a>
"""
# 提取所有链接和标题(分组匹配)
pattern = r'<a href="(.*?)">(.*?)</a>'
results = re.findall(pattern, html)
for url, title in results:
print(f"标题:{title},链接:{url}")
# 输出:
# 标题:百度,链接:https://www.baidu.com
# 标题:GitHub,链接:https://www.github.com
场景 3:日志解析(提取错误日志)
log_text = """
2025-12-12 14:00:00 [INFO] 服务启动成功
2025-12-12 14:01:00 [ERROR] 数据库连接失败:Timeout
2025-12-12 14:02:00 [WARN] 内存使用率80%
2025-12-12 14:03:00 [ERROR] API调用失败:404 Not Found
"""
# 提取所有ERROR级别的日志(时间+内容)
pattern = r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[ERROR\] (.*)"
errors = re.findall(pattern, log_text)
for time, content in errors:
print(f"错误时间:{time},错误内容:{content}")
# 输出:
# 错误时间:2025-12-12 14:01:00,错误内容:数据库连接失败:Timeout
# 错误时间:2025-12-12 14:03:00,错误内容:API调用失败:404 Not Found
六、避坑指南:90% 的新手会踩的坑
1. 混淆match和search
- 错误:用
re.match()查找字符串中间的内容(返回 None); - 解决:验证格式用
match(开头匹配),查找内容用search/findall。
2. 忘记使用 raw 字符串导致转义错误
- 错误:
re.search("\d+\.\d+", "123.45")(Python 将\d转义为普通字符); - 解决:所有正则表达式都加
r前缀,如r"\d+\.\d+"。
3. 贪婪匹配导致匹配范围超出预期
- 错误:提取
<div>内容时用.*匹配到多个标签; - 解决:加
?转为非贪婪匹配,如.*?。
4. 忽略换行符的匹配(.默认不匹配\n)
- 错误:
re.search(r"a.*b", "a\nb")返回 None; - 解决:加
re.S修饰符,re.search(r"a.*b", "a\nb", re.S)。
5. 重复编译正则导致性能低下
- 错误:循环中多次调用
re.match(pattern, text); - 解决:提前用
re.compile()编译正则,循环中复用 Pattern 对象。
七、总结
re模块是 Python 文本处理的核心工具,掌握它的关键在于:- 分清
match/search/findall的适用场景; - 熟练使用分组、贪婪 / 非贪婪匹配、模式修饰符;
- 用 raw 字符串避免转义错误,用
compile提升性能; - 结合业务场景(验证、提取、替换、分割)灵活组合用法。
浙公网安备 33010602011771号