在Python中,字符串替换是常见的操作,但简单的str.replace()方法只能处理固定字符串的替换。当需要模式匹配(如替换所有数字、邮箱、URL等)时,正则表达式(re模块)的re.sub()方法就派上了用场。本文将详细介绍如何使用正则表达式进行字符串替换,并提供实际案例。
1. 基础正则替换:re.sub()
1.1re.sub()基本语法
re.sub()是Python正则替换的核心方法,语法如下:
importre re.sub(pattern,repl,string,count=0,flags=0)pattern:正则表达式模式(匹配要替换的内容)。repl:替换内容(可以是字符串或函数)。string:待替换的原始字符串。count:最大替换次数(默认0表示全部替换)。flags:正则标志(如re.IGNORECASE忽略大小写)。
1.2 简单替换示例
(1) 替换所有数字
importre text="我的电话是123456789,QQ是987654321"new_text=re.sub(r'\d+','***',text)# \d+ 匹配1个或多个数字print(new_text)# 输出: 我的电话是***,QQ是***(2) 替换所有非字母字符
text="Hello! 123 @World#Python"new_text=re.sub(r'[^a-zA-Z]',' ',text)# [^a-zA-Z] 匹配非字母字符print(new_text)# 输出: Hello World Python(3) 忽略大小写替换
text="Python is FUN, python is powerful"new_text=re.sub(r'python','Java',text,flags=re.IGNORECASE)# 忽略大小写print(new_text)# 输出: Java is FUN, Java is powerful2. 高级替换技巧
2.1 使用分组(())和反向引用(\1,\2)
正则表达式可以用()分组,并在替换时用\1、\2引用分组内容。
示例:交换日期格式(YYYY-MM-DD → DD/MM/YYYY)
text="2023-01-01, 2024-12-31"new_text=re.sub(r'(\d{4})-(\d{2})-(\d{2})',r'\3/\2/\1',text)# \3=日, \2=月, \1=年print(new_text)# 输出: 01/01/2023, 31/12/20242.2 使用函数动态替换
repl参数可以是一个函数,根据匹配内容动态生成替换字符串。
示例:将数字乘以2后替换
defdouble_num(match):num=int(match.group())# 获取匹配的数字returnstr(num*2)text="1 apple, 2 bananas, 3 oranges"new_text=re.sub(r'\d+',double_num,text)# 调用函数替换print(new_text)# 输出: 2 apple, 4 bananas, 6 oranges示例:隐藏敏感信息(如邮箱)
defhide_email(match):email=match.group()returnemail[0]+"***"+email[-4:]# 保留首字母和后4位text="联系我:test@example.com 或 admin@site.org"new_text=re.sub(r'[\w.-]+@[\w.-]+',hide_email,text)print(new_text)# 输出: 联系我:t***mple.com 或 a***site.org2.3 限制替换次数(count参数)
text="111-222-333-444"new_text=re.sub(r'\d+','X',text,count=2)# 只替换前2个数字print(new_text)# 输出: X-X-333-4443. 实际应用案例
案例1:清理HTML标签
importre html="<p>Hello, <b>World</b>!</p>"clean_text=re.sub(r'<[^>]+>','',html)# 匹配所有HTML标签并删除print(clean_text)# 输出: Hello, World!案例2:标准化电话号码格式
text="电话:010-12345678,手机:138-1234-5678"new_text=re.sub(r'(\d{3})-(\d{4})-(\d{4})',r'\1\2\3',text)# 尝试直接替换(可能不匹配)# 更通用的方法:defnormalize_phone(match):full_num=re.sub(r'[^\d]','',match.group())# 先删除所有非数字iflen(full_num)==11:# 手机号码returnf"手机:{full_num[:3]}-{full_num[3:7]}-{full_num[7:]}"eliflen(full_num)==8:# 座机号码(简化版)returnf"电话:{full_num[:3]}-{full_num[3:]}"else:returnmatch.group()# 先提取所有电话号码(简化版,实际需更复杂正则)text_with_phones=re.sub(r'(\d{3}[-]?\d{4}[-]?\d{4})|(\d{3}[-]?\d{4})',normalize_phone,text)# 更准确的方式是分两步:# 1. 提取所有电话号码# 2. 替换print(text_with_phones)# 输出可能不符合预期,需更精细的正则# 更合理的实现(分两步):phones=re.findall(r'\d{3}[-]?\d{4}[-]?\d{4}|\d{3}[-]?\d{4}',text)forphoneinphones:cleaned=re.sub(r'[^\d]','',phone)iflen(cleaned)==11:new_phone=f"{cleaned[:3]}-{cleaned[3:7]}-{cleaned[7:]}"eliflen(cleaned)==8:new_phone=f"{cleaned[:3]}-{cleaned[3:]}"else:new_phone=phone text=text.replace(phone,new_phone)print(text)# 输出: 电话:010-12345678,手机:138-1234-5678更简洁的电话号码标准化(使用re.sub直接替换):
text="电话:010-12345678,手机:138-1234-5678"defnormalize_phone(match):num=re.sub(r'[^\d]','',match.group())iflen(num)==11:returnf"手机:{num[:3]}-{num[3:7]}-{num[7:]}"eliflen(num)==8:returnf"电话:{num[:3]}-{num[3:]}"else:returnmatch.group()# 匹配手机号(11位)或座机号(8位,可能带-)new_text=re.sub(r'(手机:)?(\d{3}[-]?\d{4}[-]?\d{4})|(电话:)?(\d{3}[-]?\d{4})',lambdam:normalize_phone(m),text)# 上述正则不够完美,更准确的方式:# 先提取所有电话号码,再替换print(new_text)# 可能需要调整正则# 更准确的实现(分两步):importre text="电话:010-12345678,手机:138-1234-5678"# 匹配手机号或座机号pattern=r'(?:电话:|手机:)?(\d{3}[-]?\d{4}[-]?\d{4}|\d{3}[-]?\d{4})'defreplace_phone(match):raw_phone=match.group(1)cleaned=re.sub(r'[^\d]','',raw_phone)iflen(cleaned)==11:returnf"手机:{cleaned[:3]}-{cleaned[3:7]}-{cleaned[7:]}"eliflen(cleaned)==8:returnf"电话:{cleaned[:3]}-{cleaned[3:]}"else:returnraw_phone new_text=re.sub(pattern,replace_phone,text)print(new_text)# 输出: 电话:010-12345678,手机:138-1234-5678简化版(假设输入格式较规范):
text="电话:010-12345678,手机:138-1234-5678"new_text=re.sub(r'(\d{3})-(\d{4})-(\d{4})',# 匹配手机号格式r'手机:\1-\2-\3',re.sub(r'(\d{3})-(\d{4})',# 匹配座机号格式r'电话:\1-\2',text))print(new_text)# 输出: 电话:010-12345678,手机:138-1234-5678案例3:替换URL中的协议(http → https)
text="访问 http://example.com 或 https://test.org"new_text=re.sub(r'http://','https://',text)# 简单替换print(new_text)# 输出: 访问 https://example.com 或 https://test.org4. 常见问题与解决方案
问题1:正则表达式匹配不到内容?
- 原因:正则模式错误或未使用
re.IGNORECASE等标志。 - 解决:使用在线正则测试工具(如regex101.com)调试。
问题2:替换后格式混乱?
- 原因:未正确使用分组或反向引用。
- 解决:检查
()分组和\1、\2是否匹配。
问题3:性能问题(大量文本替换)?
- 原因:
re.sub()在循环中调用或正则复杂度高。 - 解决:预编译正则(
re.compile())或优化正则表达式。
5. 总结
| 方法 | 适用场景 | 示例 |
|---|---|---|
re.sub(r'\d+', 'X', text) | 简单模式替换 | 替换所有数字为X |
re.sub(r'(\d{4})-(\d{2})', r'\2/\1', text) | 分组与反向引用 | 交换日期格式 |
re.sub(r'\d+', lambda m: str(int(m.group())*2), text) | 函数动态替换 | 数字乘以2后替换 |
re.sub(r'<[^>]+>', '', text) | 清理HTML标签 | 删除所有<...>标签 |
最佳实践
- 简单替换用
str.replace(),复杂模式用re.sub()。 - 需要保留部分匹配内容时,用
()分组和\1反向引用。 - 动态生成替换内容时,用函数作为
repl参数。 - 处理大量文本时,预编译正则(
re.compile())提高性能。
掌握这些技巧后,你可以高效地处理各种字符串替换需求! 🚀