pin_drop当前位置:知识文库 ❯ 图文
Python标准库re:正则表达式完全指南
一、re模块概述
re 是Python标准库中用于正则表达式操作的核心模块。正则表达式是一种强大的文本匹配工具,能够高效地完成字符串的搜索、替换、提取和验证。无论是数据清洗、日志分析、表单验证还是文本挖掘,re模块都是Python开发者的利器。
re模块的核心操作包括:匹配(判断是否符合模式)、搜索(查找第一个匹配)、查找全部(找出所有匹配)和替换(替换匹配内容)。
二、常用正则语法
1. 字符匹配
代码示例
. # 匹配除换行符外的任意单个字符
\d # 匹配数字 [0-9]
\D # 匹配非数字
\w # 匹配字母数字下划线 [a-zA-Z0-9_]
\W # 匹配非单词字符
\s # 匹配空白字符(空格、制表符、换行符)
\S # 匹配非空白字符
[abc] # 字符集,匹配a、b或c中的任意一个
[^abc] # 否定字符集,匹配除a、b、c外的字符2. 量词(重复匹配)
代码示例
* # 匹配0次或多次
+ # 匹配1次或多次
? # 匹配0次或1次
{n} # 精确匹配n次
{n,} # 匹配至少n次
{n,m} # 匹配n到m次3. 位置锚点
代码示例
^ # 匹配字符串开头
$ # 匹配字符串结尾
\b # 匹配单词边界
\B # 匹配非单词边界
(?=...) # 正向前瞻(后面跟着...)
(?!...) # 负向前瞻(后面不跟...)4. 分组与捕获
代码示例
(...) # 捕获分组,匹配内容并保存
(?:...) # 非捕获分组,匹配但不保存
(?P<name>...) # 命名捕获组
\1 # 反向引用第1个捕获组
(?P=name) # 引用命名捕获组5. 常用模式速查
三、核心函数用法
1. re.match() - 从开头匹配
代码示例
import re
# match从字符串开头匹配
result = re.match(r'\d+', '123abc')
if result:
print(f"匹配到: {result.group()}") # 输出: 123
# 开头不是数字,匹配失败
result = re.match(r'\d+', 'abc123')
print(result) # None2. re.search() - 搜索第一个匹配
代码示例
import re
# search在整个字符串中搜索第一个匹配
result = re.search(r'\d+', 'abc123def456')
if result:
print(f"匹配到: {result.group()}") # 输出: 123
print(f"位置: {result.span()}") # 输出: (3, 6)3. re.findall() - 查找所有匹配
代码示例
import re
# findall返回所有匹配结果的列表
text = "电话:13812345678,邮箱:test@example.com,手机:13987654321"
phones = re.findall(r'1[3-9]\d{9}', text)
print(f"手机号: {phones}") # ['13812345678', '13987654321']
# 带分组时返回元组列表
result = re.findall(r'(\d{4})-(\d{2})-(\d{2})', '2026-06-28 和 2026-07-01')
print(f"日期: {result}") # [('2026', '06', '28'), ('2026', '07', '01')]4. re.sub() - 替换
代码示例
import re
text = "我的电话是13812345678,备用电话13987654321"
# 将手机号中间4位替换为****
result = re.sub(r'(1[3-9]\d)\d{4}(\d{4})', r'\1****\2', text)
print(result) # 我的电话是138****5678,备用电话139****4321
# 使用函数作为替换
def mask_phone(match):
phone = match.group()
return phone[:3] + '****' + phone[7:]
result = re.sub(r'1[3-9]\d{9}', mask_phone, text)
print(result)5. re.split() - 分割
代码示例
import re
text = "苹果,香蕉;橙子 葡萄、芒果"
# 按多种分隔符分割
result = re.split(r'[,;、\s]+', text)
print(result) # ['苹果', '香蕉', '橙子', '葡萄', '芒果']
# 保留分隔符
result = re.split(r'([,;、\s]+)', text)
print(result)6. re.compile() - 预编译正则
代码示例
import re
# 预编译正则表达式,提高重复使用时的效率
phone_pattern = re.compile(r'1[3-9]\d{9}')
text1 = "联系电话:13812345678"
text2 = "备用号码:13987654321"
print(phone_pattern.findall(text1)) # ['13812345678']
print(phone_pattern.findall(text2)) # ['13987654321']
print(phone_pattern.search(text1).group()) # 13812345678四、完整代码示例
示例1:邮箱验证器
代码示例
import re
def validate_email(email):
"""验证邮箱格式是否正确"""
pattern = r'^[\w.-]+@[\w.-]+\.\w+$'
if re.match(pattern, email):
return True, f"{email} 是有效的邮箱地址"
else:
return False, f"{email} 不是有效的邮箱地址"
# 测试
emails = ["test@example.com", "user.name@domain.cn", "invalid-email", "@no-local.com"]
for email in emails:
is_valid, msg = validate_email(email)
print(msg)示例2:从HTML中提取链接
代码示例
import re
html = '''
<a href="https://www.example.com">首页</a>
<a href="/about">关于我们</a>
<a href="https://blog.example.com/post/1">文章1</a>
<a href="mailto:test@example.com">联系邮箱</a>
'''
# 提取所有href属性值
links = re.findall(r'href=["\']([^"\']+)["\']', html)
print("所有链接:")
for link in links:
print(f" {link}")
# 只提取http/https链接
http_links = re.findall(r'href=["\'](https?://[^"\']+)["\']', html)
print("\nHTTP链接:")
for link in http_links:
print(f" {link}")示例3:日志解析器
代码示例
import re
log_text = """
[2026-06-28 10:30:45] INFO: 用户登录成功 user_id=1001
[2026-06-28 10:31:02] ERROR: 数据库连接失败 code=500
[2026-06-28 10:31:15] WARNING: 内存使用率过高 usage=85%
[2026-06-28 10:32:00] INFO: 数据备份完成 size=1.2GB
"""
# 解析日志
log_pattern = re.compile(r'\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+)')
for line in log_text.strip().split('\n'):
match = log_pattern.match(line)
if match:
time, level, message = match.groups()
print(f"时间: {time}, 级别: {level}, 消息: {message}")
# 统计各级别日志数量
levels = re.findall(r'\] (\w+):', log_text)
from collections import Counter
print(f"\n日志级别统计: {dict(Counter(levels))}")示例4:敏感信息脱敏
代码示例
import re
def desensitize(text):
"""对文本中的敏感信息进行脱敏处理"""
# 手机号脱敏:138****5678
text = re.sub(r'(1[3-9]\d)\d{4}(\d{4})', r'\1****\2', text)
# 身份证号脱敏:110***********1234
text = re.sub(r'(\d{3})\d{11}(\d{4})', r'\1***********\2', text)
# 邮箱脱敏:t***@example.com
def mask_email(match):
email = match.group()
local, domain = email.split('@')
if len(local) > 1:
masked = local[0] + '*' * (len(local) - 1)
else:
masked = '*'
return f"{masked}@{domain}"
text = re.sub(r'[\w.-]+@[\w.-]+\.\w+', mask_email, text)
return text
# 测试
original = "用户张三,手机13812345678,邮箱zhangsan@example.com,身份证110101199001011234"
print(f"原始: {original}")
print(f"脱敏: {desensitize(original)}")五、注意事项与最佳实践
注意1:正则表达式字符串前务必加
r前缀(原始字符串),如r'\d+'。否则反斜杠会被Python字符串转义,导致匹配失败。
注意2:
re.match()只从字符串开头匹配,如果开头不匹配就返回None。如果需要在整个字符串中搜索,请使用re.search()。
注意3:
*、+、?默认是贪婪匹配,会尽可能多地匹配字符。如需非贪婪匹配,在后面加?,如.*?。
注意4:对于需要重复使用的正则表达式,建议使用
re.compile()预编译,可以提升匹配效率。
小贴士
编写复杂正则表达式时,可以使用 re.VERBOSE(或 re.X)标志,允许在正则中添加空白和注释,大幅提高可读性。例如:re.compile(r'\d{4} # 年 -\d{2} # 月', re.X)。
六、小结
-
核心函数:
match()开头匹配、search()搜索、findall()查找全部、sub()替换、split()分割 -
常用语法:
\d数字、\w单词字符、+一次以上、*零次以上、()分组 -
原始字符串:正则前加
r前缀避免转义问题 -
贪婪与非贪婪:默认贪婪匹配,加
?变为非贪婪 -
预编译:使用
re.compile()提高重复使用效率
七、练习题
练习1
编写一个函数,验证用户输入的字符串是否符合中国大陆手机号格式(11位,以1开头,第二位为3-9)。
练习2
编写一个程序,从一段文本中提取所有的日期(格式:YYYY-MM-DD),并按时间顺序排序输出。
练习3
编写一个Markdown标题提取器,能够从Markdown文本中提取所有以#开头的标题行,并返回标题级别和标题内容。
常见问题
re.match和re.search有什么区别?
re.match() 只从字符串开头匹配,如果开头不匹配就返回None;re.search() 会扫描整个字符串,找到第一个匹配。如果需要验证字符串是否以某模式开头,用match;如果需要在任意位置查找,用search。
为什么正则前要加r前缀?
加 r 前缀表示原始字符串(raw string),这样反斜杠 \\ 不会被Python解释为转义字符。例如 r'\d' 表示匹配数字,而 '\d' 可能被解释为转义序列导致错误。
贪婪匹配和非贪婪匹配有什么区别?
贪婪匹配(默认)会尽可能多地匹配字符,如 .* 会匹配到最后一个可能的位置;非贪婪匹配(加 ?)会尽可能少地匹配,如 .*? 匹配到第一个满足条件的位置就停止。提取HTML标签内容时通常用非贪婪。
如何匹配中文字符?
使用 [\u4e00-\u9fa5] 匹配中文字符。例如 re.findall(r'[\u4e00-\u9fa5]+', text) 可以提取文本中的所有中文词语。注意 \w 在Python 3中默认支持Unicode,也能匹配中文。
如何处理正则表达式的特殊字符?
使用 re.escape() 函数可以自动转义字符串中的所有正则特殊字符。例如 re.escape('hello.world') 返回 'hello\\.world',这样点号就只匹配字面量点号而非任意字符。
本文涉及AI创作
内容由AI创作,请仔细甄别