【re,一个正则表达式的 Python 库!】文本处理的瑞士军刀
在编程世界里,有一项技能一旦掌握,就会让你在处理文本时拥有“降维打击”的能力——这就是正则表达式。而Python标准库中的re模块,正是正则表达式在Python生态中的官方实现。无论你是需要从一大段日志中提取IP地址、验证用户输入的手机号格式、批量替换文档中的敏感词,还是从杂乱无章的网页源码里抓取邮箱,re都能用简洁而强大的模式匹配语法解决这些看似复杂的文本问题。
在实际生活中,re的应用场景几乎无处不在:后端开发用它做表单输入的合法性校验(邮箱、手机号、身份证);运维工程师用它从海量日志中grep出异常堆栈;数据清洗员用它批量规范化CSV中的日期格式;爬虫工程师用它从HTML中精准提取价格、评论数等结构化数据;自然语言处理用它做分词前的文本预处理;甚至你每天使用的IDE或编辑器的“查找替换”功能中的“使用正则表达式”选项,背后也是类似re的引擎。它的核心价值在于用一套模式语言描述无穷的文本规律——原本需要几十行字符串切片和条件判断才能完成的任务,用正则往往一两行就能优雅解决。
下面将从最基础的匹配开始,带你系统掌握Python中正则表达式的正确打开方式。
一、安装库
re是Python的标准库模块,无需任何安装,直接导入即可:
python
import re
print("re模块已就绪,版本:", re.__version__) # 注意re没有独立版本,随Python发行
二、基本用法——4步从零构建第一个正则匹配
以下4个步骤,让你快速掌握re模块的核心API:match、search、findall和sub。
1. 使用re.match()从字符串开头匹配
match()从字符串的起始位置开始匹配,如果开头不满足模式,则返回None。
python
import re
pattern = r'\d+' # 匹配一个或多个数字
text = '123abc456'
result = re.match(pattern, text)
if result:
print(f"匹配成功: {result.group()}") # 输出: 123
else:
print("匹配失败")
注意:原始字符串
r''中的反斜杠不会被转义,推荐在正则表达式中始终使用。
2. 使用re.search()扫描整个字符串
search()扫描整个字符串,返回第一个匹配的位置。
python
text = '订单号: ABC-12345-XYZ'
pattern = r'\d{5}' # 匹配连续5个数字
match = re.search(pattern, text)
if match:
print(f"找到: {match.group()}") # 输出: 12345
print(f"起始索引: {match.start()}, 结束索引: {match.end()}")
else:
print("未找到")
3. 使用re.findall()获取所有匹配结果
findall()返回所有非重叠匹配的列表,是最常用的提取函数。
python
text = '我的邮箱是: alice@example.com, bob@gmail.com, chris@163.com'
pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
emails = re.findall(pattern, text)
print(emails)
# 输出: ['alice@example.com', 'bob@gmail.com', 'chris@163.com']
4. 使用re.sub()进行替换
sub()将匹配到的内容替换为指定字符串,是文本清洗的利器。
python
text = '联系电话: 138-1234-5678, 办公室: 010-87654321'
# 将所有电话号码的连字符替换为空,只保留数字
cleaned = re.sub(r'-', '', text)
print(cleaned) # 输出: 联系电话: 13812345678, 办公室: 01087654321
# 结合分组实现更灵活的替换
text2 = '2024-05-12, 2025-12-25'
# 将日期格式从 YYYY-MM-DD 转为 DD/MM/YYYY
converted = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', text2)
print(converted) # 输出: 12/05/2024, 25/12/2025
三、高级用法——让正则如虎添翼
1. 编译正则表达式:re.compile()
当同一个正则表达式需要重复使用时,提前编译可以提升性能,并使代码更清晰。
python
# 编译一个邮箱正则
email_regex = re.compile(r'\b[\w.%+-]+@[\w.-]+\.[A-Za-z]{2,}\b')
text = '联系: support@company.com, 投诉: feedback@company.com'
matches = email_regex.findall(text)
print(matches) # ['support@company.com', 'feedback@company.com']
# 编译时可以指定标志,如忽略大小写
case_insensitive = re.compile(r'python', re.IGNORECASE)
print(case_insensitive.findall('I love Python and PYTHON')) # ['Python', 'PYTHON']
2. 分组捕获与命名分组
括号()用于分组,可以提取子模式;(?P<name>)可以给分组命名,让代码更可读。
python
text = '姓名: 张三, 年龄: 28, 邮箱: zhangsan@163.com'
# 普通分组
pattern = r'姓名: (.+), 年龄: (\d+), 邮箱: (.+)'
match = re.search(pattern, text)
if match:
print(f"姓名: {match.group(1)}") # 张三
print(f"年龄: {match.group(2)}") # 28
print(f"邮箱: {match.group(3)}") # zhangsan@163.com
# 命名分组
pattern_named = r'姓名: (?P<name>.+), 年龄: (?P<age>\d+), 邮箱: (?P<email>.+)'
match = re.search(pattern_named, text)
if match:
print(match.group('name'), match.group('age'), match.group('email'))
# 获取所有分组字典
print(match.groupdict()) # {'name': '张三', 'age': '28', 'email': 'zhangsan@163.com'}
3. 正则标志:多行、点号匹配换行、详细模式
python
text = '''第一行
第二行
第三行'''
# re.MULTILINE: ^ 和 $ 匹配每行的开头/结尾
multi_pattern = re.compile(r'^\w+', re.MULTILINE)
print(multi_pattern.findall(text)) # ['第一行', '第二行', '第三行']
# re.DOTALL: 让 . 匹配包括换行在内的所有字符
dotall_pattern = re.compile(r'第一行(.*?)第三行', re.DOTALL)
print(dotall_pattern.search(text).group(1)) # 匹配换行符在内的中间内容
# re.VERBOSE: 允许正则中包含空白和注释,提升可读性
verbose_pattern = re.compile(r"""
\d{3} # 区号
- # 连字符
\d{8} # 号码
""", re.VERBOSE)
print(verbose_pattern.findall('电话: 010-12345678')) # ['010-12345678']
4. 贪婪与非贪婪匹配
*、+、?、{m,n}默认是贪婪的(尽可能多匹配),在后面加?变为非贪婪(尽可能少匹配)。
python
html = '<div>内容1</div><div>内容2</div>' # 贪婪匹配:匹配尽可能长的字符串 greedy = re.findall(r'<div>.*</div>', html) print(greedy) # ['<div>内容1</div><div>内容2</div>'] # 非贪婪匹配:匹配最短的合规字符串 non_greedy = re.findall(r'<div>.*?</div>', html) print(non_greedy) # ['<div>内容1</div>', '<div>内容2</div>']
四、实际应用场景——三个有深度的案例,日常就能用
场景一:从杂乱文本中提取所有URL
无论是爬虫还是日志分析,提取文本中的URL是一个经典需求。
python
import re
def extract_urls(text):
# 该正则匹配http/https/ftp开头的URL,支持常见字符
url_pattern = r'https?://[^\s<>"\'{}|\\^`\[\]]+'
urls = re.findall(url_pattern, text)
return list(set(urls)) # 去重
sample = """
请访问我们的官网 https://www.example.com 获取更多信息。
你也可以使用 http://docs.python.org 查阅文档。
另外,内部地址 http://192.168.1.1/admin 仅内网访问。
重复的官网 https://www.example.com 会去重。
"""
print(extract_urls(sample))
# 输出: ['http://192.168.1.1/admin', 'https://www.example.com', 'http://docs.python.org']
场景二:验证并清洗用户输入(手机号/身份证)
在日常表单验证中,正则是最便捷的校验手段。
python
import re
def validate_phone(phone):
# 中国大陆手机号:1开头,第二位3-9,后面9位数字
pattern = r'^1[3-9]\d{9}$'
if re.match(pattern, phone):
return True, "手机号有效"
else:
return False, "手机号格式错误"
def mask_id_card(id_num):
# 将身份证号中间8位替换为********
pattern = r'(\d{4})\d{10}(\d{4})' # 前4位+后4位保留,中间10位变成8个*
masked = re.sub(pattern, r'\1********\2', id_num)
return masked
print(validate_phone('13812345678')) # (True, '手机号有效')
print(validate_phone('12345678901')) # (False, '手机号格式错误')
print(mask_id_card('11010119900307663X')) # 1101********7663X
场景三:解析日志文件中的错误行与时间戳
运维经常需要从大型日志中筛选出ERROR级别的日志,并提取发生时间。
python
import re
log_sample = """
2025-05-12 08:30:15 INFO 服务启动成功
2025-05-12 09:45:22 ERROR 数据库连接超时,重试中
2025-05-12 10:02:07 WARN 磁盘使用率85%
2025-05-12 10:15:39 ERROR 用户登录失败,密码错误
"""
# 匹配ERROR级别所在的行,并提取时间和消息
error_pattern = re.compile(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+ERROR\s+(.+)')
errors = []
for line in log_sample.splitlines():
match = error_pattern.search(line)
if match:
timestamp, message = match.groups()
errors.append({'time': timestamp, 'message': message})
for err in errors:
print(f"[{err['time']}] 错误: {err['message']}")
# 输出:
# [2025-05-12 09:45:22] 错误: 数据库连接超时,重试中
# [2025-05-12 10:15:39] 错误: 用户登录失败,密码错误
五、结尾:总结与互动
re模块是Python文本处理能力皇冠上的明珠。它以极少的代码量完成看似复杂的匹配、查找、替换任务,是日常自动化脚本、数据清洗、爬虫、表单验证等场景中不可或缺的工具。但正则表达式也是一把双刃剑——过于复杂的正则不仅难以调试,还可能造成灾难性的回溯性能问题(例如(a+)+b匹配超长字符串)。因此,建议在编写正则时遵循“简单够用”的原则:能用字符串方法解决的(如startswith、in)就不用正则;对于复杂正则,拆分成多个小正则,并利用re.VERBOSE添加注释。
现在,轮到你把正则用起来了:有没有遇到过需要从一大段文本里提取结构化数据的需求?比如提取某个应用日志中的所有IP地址、解析商品页面里的价格数字、或者验证用户输入的密码强度(至少一个大写、一个小写、一个数字、长度8-16位)?欢迎在评论区分享你曾经用正则解决过的真实问题,或者留下你在使用re时遇到的疑难杂症(比如re.match和re.search什么时候用、.*?到底怎么工作)——我会挑选经典问题,在后续文章中详细解答。快去尝试用正则优雅地处理你身边的文本吧!
更多推荐

所有评论(0)