Python 正则表达式实战:从入门到精通
引言
大家好,我是一名正在从Rust转向Python的后端开发者。在日常开发中,字符串处理是必不可少的环节,而正则表达式就是处理字符串的一把利器。作为从Rust过来的开发者,我发现Python的正则表达式模块re虽然语法上与Rust的regex crate有所不同,但功能同样强大。今天,我想和大家分享一下我在Python正则表达式学习和实践中的一些经验。
正则表达式基础
什么是正则表达式?
正则表达式(Regular Expression)是一种用于匹配字符串模式的工具。它可以帮助我们快速地进行字符串搜索、替换、验证等操作。
Python中的正则表达式模块
Python提供了re模块来支持正则表达式操作。让我们先来看一个简单的例子:
import re
# 匹配邮箱地址
pattern = r'[\w.-]+@[\w.-]+\.\w+'
text = '请联系我:john.doe@example.com 或 jane_smith@company.org'
matches = re.findall(pattern, text)
print(matches) # 输出: ['john.doe@example.com', 'jane_smith@company.org']
常用正则表达式语法
字符类
| 语法 | 含义 | 示例 |
|---|---|---|
. | 匹配任意字符(除换行符) | a.c 匹配 abc, a1c |
\d | 匹配数字 | \d{3} 匹配3位数字 |
\w | 匹配字母、数字、下划线 | \w+ 匹配单词 |
\s | 匹配空白字符 | \s+ 匹配多个空格 |
[abc] | 匹配字符集中的任意一个 | [aeiou] 匹配元音 |
[^abc] | 匹配不在字符集中的字符 | [^0-9] 匹配非数字 |
量词
| 语法 | 含义 | 示例 |
|---|---|---|
* | 匹配0次或多次 | a* 匹配 '', a, aa |
+ | 匹配1次或多次 | a+ 匹配 a, aa, aaa |
? | 匹配0次或1次 | colou?r 匹配 color, colour |
{n} | 精确匹配n次 | a{3} 匹配 aaa |
{n,m} | 匹配n到m次 | a{2,4} 匹配 aa, aaa, aaaa |
锚点
| 语法 | 含义 | 示例 |
|---|---|---|
^ | 匹配字符串开头 | ^Hello 匹配以Hello开头 |
$ | 匹配字符串结尾 | world$ 匹配以world结尾 |
\b | 匹配单词边界 | \bword\b 匹配独立单词 |
分组与捕获
# 使用分组提取信息
pattern = r'(\d{4})-(\d{2})-(\d{2})'
text = '日期:2026-05-08'
match = re.search(pattern, text)
if match:
year = match.group(1) # '2026'
month = match.group(2) # '05'
day = match.group(3) # '08'
print(f'{year}年{month}月{day}日')
实际应用场景
场景1:数据清洗与提取
假设我们有一段包含电话号码的文本,需要提取所有中国大陆手机号码:
import re
text = '''联系我们:
手机:13812345678
电话:010-12345678
备用手机:15987654321
传真:021-87654321'''
# 匹配中国大陆手机号(以1开头,11位数字)
pattern = r'1[3-9]\d{9}'
phones = re.findall(pattern, text)
print(phones) # 输出: ['13812345678', '15987654321']
场景2:表单验证
在Web开发中,我们经常需要验证用户输入:
def validate_email(email):
pattern = r'^[\w.-]+@[\w.-]+\.\w+$'
return bool(re.match(pattern, email))
def validate_url(url):
pattern = r'^https?://[\w.-]+(\.[\w.-]+)+[/\w.-]*$'
return bool(re.match(pattern, url))
# 测试
print(validate_email('test@example.com')) # True
print(validate_email('invalid-email')) # False
print(validate_url('https://www.example.com/path')) # True
场景3:文本替换
将文本中的HTML标签去除:
import re
html_text = '<p>Hello <strong>World</strong>!</p>'
# 去除HTML标签
clean_text = re.sub(r'<[^>]+>', '', html_text)
print(clean_text) # 输出: 'Hello World!'
场景4:日志分析
从日志文件中提取关键信息:
import re
log_text = '''
2026-05-08 10:30:23 INFO [main] User 'admin' logged in from 192.168.1.100
2026-05-08 10:35:45 ERROR [api] Database connection failed
2026-05-08 10:40:00 WARN [worker] High memory usage detected: 85%
'''
# 提取日志级别和消息
pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (\w+) \[(\w+)\] (.+)'
matches = re.findall(pattern, log_text)
for match in matches:
timestamp, level, module, message = match
print(f'[{level}] {message}')
高级技巧
命名分组
使用命名分组可以让代码更清晰:
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
text = '2026-05-08'
match = re.match(pattern, text)
if match:
print(match.group('year')) # '2026'
print(match.group('month')) # '05'
print(match.group('day')) # '08'
print(match.groupdict()) # {'year': '2026', 'month': '05', 'day': '08'}
非贪婪匹配
默认情况下,正则表达式是贪婪的,会匹配尽可能多的字符:
text = '<div>content1</div><div>content2</div>'
# 贪婪匹配(默认)
greedy_pattern = r'<div>.*</div>'
print(re.findall(greedy_pattern, text))
# 输出: ['<div>content1</div><div>content2</div>']
# 非贪婪匹配
non_greedy_pattern = r'<div>.*?</div>'
print(re.findall(non_greedy_pattern, text))
# 输出: ['<div>content1</div>', '<div>content2</div>']
正向预查和负向预查
# 正向预查:匹配后面跟着特定模式的内容
pattern = r'\d+(?=元)'
text = '价格:100元,优惠价:80元'
print(re.findall(pattern, text)) # 输出: ['100', '80']
# 负向预查:匹配后面不跟着特定模式的内容
pattern = r'\d+(?!元)'
text = '数量:50个,价格:100元'
print(re.findall(pattern, text)) # 输出: ['50']
编译正则表达式
对于需要多次使用的正则表达式,预编译可以提高性能:
import re
# 预编译正则表达式
pattern = re.compile(r'[\w.-]+@[\w.-]+\.\w+')
# 多次使用
text1 = '联系邮箱:test@example.com'
text2 = '备用邮箱:admin@company.org'
print(pattern.findall(text1)) # ['test@example.com']
print(pattern.findall(text2)) # ['admin@company.org']
常见问题与解决方案
问题1:转义字符问题
Python字符串中的反斜杠需要转义,使用原始字符串可以避免这个问题:
# 不推荐:需要双重转义
pattern = '\\d+'
# 推荐:使用原始字符串
pattern = r'\d+'
问题2:性能问题
对于非常大的文本或复杂的正则表达式,可能会遇到性能问题:
import re
import time
# 优化前:复杂正则表达式
pattern = r'(a|b|c|d|e)+'
text = 'a' * 100
start = time.time()
re.match(pattern, text)
print(f'耗时: {time.time() - start:.2f}秒')
# 优化后:简化正则表达式
pattern = r'[abcde]+'
start = time.time()
re.match(pattern, text)
print(f'优化后耗时: {time.time() - start:.2f}秒')
问题3:贪婪匹配导致的意外结果
text = 'Hello World!'
# 贪婪匹配可能导致意外结果
pattern = r'H.*o'
print(re.findall(pattern, text)) # ['Hello Wo']
# 使用非贪婪匹配
pattern = r'H.*?o'
print(re.findall(pattern, text)) # ['Hello']
实战项目:URL参数解析器
让我们创建一个实用的URL参数解析器:
import re
from urllib.parse import urlparse
def parse_url_parameters(url):
"""解析URL中的查询参数"""
parsed = urlparse(url)
query_string = parsed.query
# 匹配key=value模式
pattern = r'([^=&]+)=([^&]*)'
matches = re.findall(pattern, query_string)
# 转换为字典
params = {}
for key, value in matches:
params[key] = value
return params
# 测试
url = 'https://www.example.com/search?q=python&page=2&sort=desc'
params = parse_url_parameters(url)
print(params) # {'q': 'python', 'page': '2', 'sort': 'desc'}
与Rust正则表达式的对比
作为从Rust转向Python的开发者,我想分享一下两者的对比:
| 特性 | Python re | Rust regex |
|---|---|---|
| 语法 | 传统正则表达式 | PCRE兼容 |
| 性能 | 一般 | 非常快 |
| 编译 | 运行时编译 | 编译时验证 |
| Unicode支持 | 需要指定标志 | 默认支持 |
| 错误处理 | 返回None或空列表 | 返回Result |
总结
正则表达式是Python开发者必备的技能之一。通过掌握正则表达式,我们可以高效地处理各种字符串操作任务。
在实际应用中,我建议:
- 对于简单的字符串操作,优先使用字符串方法(如
str.find(),str.split()等) - 对于复杂的模式匹配,使用正则表达式
- 对于需要多次使用的正则表达式,进行预编译
- 注意贪婪匹配和非贪婪匹配的区别
- 使用命名分组提高代码可读性
希望这篇文章能帮助你更好地理解和应用Python正则表达式。如果你有任何问题或建议,欢迎在评论区留言讨论!
延伸阅读:
2万+

被折叠的 条评论
为什么被折叠?



