Python3 re 模块

 Python 的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. 混淆matchsearch

  • 错误:用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 文本处理的核心工具,掌握它的关键在于:
 
  1. 分清match/search/findall的适用场景;
  2. 熟练使用分组、贪婪 / 非贪婪匹配、模式修饰符;
  3. 用 raw 字符串避免转义错误,用compile提升性能;
  4. 结合业务场景(验证、提取、替换、分割)灵活组合用法。

posted on 2025-12-12 09:06  小陶coding  阅读(48)  评论(0)    收藏  举报