Python正则表达式全面指南:re标准库详解与实践
正则表达式是处理字符串的强大工具,Python通过内置的re模块提供了完整的正则表达式功能。本文将全面介绍正则表达式的基础语法、re模块的核心函数以及实际应用场景。
一、正则表达式基础语法
1.1 基本元字符
正则表达式由普通字符和元字符组成,元字符具有特殊含义:
| 元字符 | 功能说明 | 示例 |
|---|---|---|
. |
匹配任意单个字符(除换行符) | a.c 匹配 "abc"、"a c" |
^ |
匹配字符串开头 | ^Hello 匹配以"Hello"开头的字符串 |
$ |
匹配字符串结尾 | world$ 匹配以"world"结尾的字符串 |
* |
匹配前一个字符0次或多次 | ab*c 匹配 "ac"、"abc"、"abbc" |
+ |
匹配前一个字符1次或多次 | ab+c 匹配 "abc"、"abbc" |
? |
匹配前一个字符0次或1次 | ab?c 匹配 "ac"、"abc" |
{m,n} |
匹配前一个字符m到n次 | a{2,4} 匹配 "aa"、"aaa"、"aaaa" |
1.2 字符集与预定义字符类
python
import re
# 字符集示例
text = "cat bat hat rat"
pattern1 = r'[bc]at' # 匹配"bat"或"cat"
matches1 = re.findall(pattern1, text)
print(f"字符集匹配: {matches1}") # 输出: ['cat', 'bat']
# 预定义字符类
text2 = "价格: $100, 重量: 5kg, 日期: 2024-01-01"
pattern2 = r'\d+' # 匹配一个或多个数字
matches2 = re.findall(pattern2, text2)
print(f"数字匹配: {matches2}") # 输出: ['100', '5', '2024', '01', '01']
常用预定义字符类:
| 字符类 | 等价表示 | 说明 |
|---|---|---|
\d |
[0-9] |
数字字符 |
\D |
[^0-9] |
非数字字符 |
\w |
[a-zA-Z0-9_] |
单词字符 |
\W |
[^a-zA-Z0-9_] |
非单词字符 |
\s |
`[ \t | |
| \r\f\v]` | 空白字符 | |
\S |
`[^ \t | |
| \r\f\v]` | 非空白字符 |
二、re模块核心函数详解
2.1 匹配函数对比
| 函数 | 功能 | 返回值 | 适用场景 |
|---|---|---|---|
re.match() |
从字符串起始位置匹配 | Match对象或None | 验证字符串格式 |
re.search() |
扫描整个字符串查找匹配 | Match对象或None | 查找第一个匹配项 |
re.findall() |
查找所有匹配项 | 列表 | 提取所有符合条件的内容 |
re.finditer() |
查找所有匹配项 | 迭代器 | 处理大量匹配时节省内存 |
re.sub() |
替换匹配项 | 替换后的字符串 | 字符串清洗和格式化 |
re.split() |
根据模式分割字符串 | 列表 | 复杂字符串分割 |
2.2 re.match() - 起始位置匹配
re.match()从字符串的起始位置开始匹配,如果起始位置不符合模式,则返回None。
python
import re
# re.match() 示例
def test_match():
patterns = [
r'hello',
r'\d+',
r'^Python'
]
test_strings = [
"hello world",
"123abc",
"Python is great",
"Hi Python"
]
for test_str in test_strings:
print(f"
测试字符串: '{test_str}'")
for pattern in patterns:
match = re.match(pattern, test_str)
if match:
print(f" 模式 '{pattern}' 匹配成功: {match.group()}")
else:
print(f" 模式 '{pattern}' 匹配失败")
test_match()
2.3 re.search() - 全局搜索匹配
re.search()扫描整个字符串,返回第一个匹配的对象。
python
import re
# re.search() 示例
def test_search():
text = "我的电话是138-1234-5678,另一个电话是139-8765-4321"
# 搜索电话号码
phone_pattern = r'\d{3}-\d{4}-\d{4}'
match = re.search(phone_pattern, text)
if match:
print(f"找到电话号码: {match.group()}") # 输出: 找到电话号码: 138-1234-5678
print(f"匹配位置: {match.start()} - {match.end()}") # 输出: 匹配位置: 5 - 18
print(f"匹配范围: {match.span()}") # 输出: 匹配范围: (5, 18)
else:
print("未找到电话号码")
test_search()
2.4 re.findall() - 查找所有匹配
re.findall()返回字符串中所有非重叠匹配的列表。
python
import re
# re.findall() 示例
def test_findall():
# 示例1: 提取所有电子邮件
text1 = "联系我们: admin@example.com, sales@company.org, support@test.cn"
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
emails = re.findall(email_pattern, text1)
print(f"找到的邮箱: {emails}")
# 示例2: 提取HTML标签中的内容
html_text = "<h1>标题</h1><p>段落内容</p><div>容器</div>"
tag_content = re.findall(r'<[^>]+>([^<]+)</[^>]+>', html_text)
print(f"标签内容: {tag_content}")
# 示例3: 提取价格信息
price_text = "商品A: ¥100.50, 商品B: $200.75, 商品C: €150.00"
prices = re.findall(r'[¥$€](\d+\.\d+)', price_text)
print(f"价格: {prices}")
test_findall()
2.5 re.finditer() - 返回匹配迭代器
re.finditer()返回一个迭代器,逐个产生Match对象,适合处理大量数据。
python
import re
# re.finditer() 示例
def test_finditer():
text = """
用户信息:
姓名: 张三, 年龄: 25, 城市: 北京
姓名: 李四, 年龄: 30, 城市: 上海
姓名: 王五, 年龄: 28, 城市: 广州
"""
# 使用命名分组提取信息
pattern = r'姓名:\s*(?P<name>\w+),\s*年龄:\s*(?P<age>\d+),\s*城市:\s*(?P<city>\w+)'
print("用户信息提取:")
for match in re.finditer(pattern, text):
name = match.group('name')
age = match.group('age')
city = match.group('city')
print(f" {name}, {age}岁, 来自{city}")
test_finditer()
2.6 re.sub() - 替换匹配内容
re.sub()用于替换字符串中的匹配项。
python
import re
# re.sub() 示例
def test_sub():
# 示例1: 敏感信息脱敏
text1 = "银行卡号: 6225-3801-2345-6789, 身份证: 110101199001011234"
# 银行卡号脱敏
desensitized_bank = re.sub(r'\d{4}-\d{4}-\d{4}-', '****-****-****-', text1)
# 身份证号脱敏
desensitized_id = re.sub(r'(\d{6})\d{8}(\d{4})', r'\1********\2', desensitized_bank)
print(f"脱敏后: {desensitized_id}")
# 示例2: 日期格式转换
date_text = "今天是2024/01/15,会议在2024-02-20"
standardized_date = re.sub(r'(\d{4})[/-](\d{2})[/-](\d{2})', r'\1年\2月\3日', date_text)
print(f"日期标准化: {standardized_date}")
# 示例3: 使用函数进行复杂替换
def multiply_numbers(match):
number = int(match.group())
return str(number * 2)
math_text = "数字: 10, 20, 30"
doubled_text = re.sub(r'\d+', multiply_numbers, math_text)
print(f"数字加倍: {doubled_text}")
test_sub()
2.7 re.split() - 模式分割字符串
re.split()使用正则表达式模式分割字符串。
python
import re
# re.split() 示例
def test_split():
# 示例1: 复杂分隔符分割
text1 = "苹果,香蕉;橙子|葡萄 西瓜"
fruits = re.split(r'[,;|\s]+', text1)
print(f"水果列表: {fruits}")
# 示例2: 保留分隔符
text2 = "100+200-300*400/500"
# 使用捕获分组保留运算符
parts = re.split(r'([+*-/])', text2)
print(f"分割结果: {parts}")
# 示例3: 多空格分割
text3 = "这 是 一个 有很多 空格的 文本"
words = re.split(r'\s+', text3)
print(f"单词列表: {words}")
test_split()
三、高级功能与实战应用
3.1 分组与捕获
分组是正则表达式的重要特性,可以提取子匹配内容。
python
import re
# 分组捕获示例
def group_demo():
# 提取日志信息
log_entry = "2024-01-15 14:30:25 [INFO] User login successful (user_id: 12345)"
# 使用命名分组
log_pattern = r'(?P<date>\d{4}-\d{2}-\d{2})\s+(?P<time>\d{2}:\d{2}:\d{2})\s+\[(?P<level>\w+)\]\s+(?P<message>.*?)\s+\(user_id:\s*(?P<user_id>\d+)\)'
match = re.search(log_pattern, log_entry)
if match:
print("日志解析结果:")
print(f" 日期: {match.group('date')}")
print(f" 时间: {match.group('time')}")
print(f" 级别: {match.group('level')}")
print(f" 消息: {match.group('message')}")
print(f" 用户ID: {match.group('user_id')}")
# 非捕获分组
text = "color colour"
# 使用非捕获分组 (?:)
pattern = r'col(?:ou)?r'
matches = re.findall(pattern, text)
print(f"颜色匹配: {matches}")
group_demo()
3.2 贪婪匹配与非贪婪匹配
正则表达式默认使用贪婪匹配,可以通过?改为非贪婪匹配。
python
import re
# 贪婪vs非贪婪匹配
def greedy_vs_lazy():
html_text = "<div>内容1</div><div>内容2</div><div>内容3</div>"
# 贪婪匹配 - 匹配最长的可能
greedy_pattern = r'<div>.*</div>'
greedy_match = re.search(greedy_pattern, html_text)
print(f"贪婪匹配: {greedy_match.group() if greedy_match else '无匹配'}")
# 非贪婪匹配 - 匹配最短的可能
lazy_pattern = r'<div>.*?</div>'
lazy_matches = re.findall(lazy_pattern, html_text)
print(f"非贪婪匹配: {lazy_matches}")
greedy_vs_lazy()
3.3 标志参数的使用
re模块支持多种标志参数来改变匹配行为。
python
import re
# 标志参数示例
def flags_demo():
text = "Hello
World
Python"
# 多行模式 - 让^和$匹配每行的开头和结尾
multiline_matches = re.findall(r'^\w+', text, re.MULTILINE)
print(f"多行模式匹配: {multiline_matches}")
# 忽略大小写
case_insensitive = re.findall(r'hello', text, re.IGNORECASE)
print(f"忽略大小写匹配: {case_insensitive}")
# 点号匹配所有字符(包括换行符)
dotall_match = re.search(r'Hello.*Python', text, re.DOTALL)
print(f"点号匹配所有: {dotall_match.group() if dotall_match else '无匹配'}")
flags_demo()
3.4 编译正则表达式
对于需要重复使用的模式,可以先编译再使用,提高效率。
python
import re
import time
# 编译正则表达式示例
def compile_demo():
# 准备测试数据
emails = [f"user{i}@example.com" for i in range(1000)]
# 未编译的方式
start_time = time.time()
pattern = r'^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'
valid_emails = []
for email in emails:
if re.match(pattern, email):
valid_emails.append(email)
uncompiled_time = time.time() - start_time
# 编译后的方式
start_time = time.time()
compiled_pattern = re.compile(pattern)
valid_emails_compiled = []
for email in emails:
if compiled_pattern.match(email):
valid_emails_compiled.append(email)
compiled_time = time.time() - start_time
print(f"未编译方式耗时: {uncompiled_time:.4f}秒")
print(f"编译后方式耗时: {compiled_time:.4f}秒")
print(f"性能提升: {(uncompiled_time - compiled_time)/uncompiled_time*100:.1f}%")
compile_demo()
四、实战应用案例
4.1 数据验证
python
import re
# 数据验证函数
def data_validation():
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
def validate_phone(phone):
pattern = r'^1[3-9]\d{9}$' # 中国手机号
return bool(re.match(pattern, phone))
def validate_id_card(id_card):
pattern = r'^\d{17}[\dXx]$' # 简化版身份证验证
return bool(re.match(pattern, id_card))
# 测试验证
test_data = [
"test@example.com",
"13812345678",
"110101199001011234",
"invalid-email",
"12345678901",
"ABC12345678901234"
]
print("数据验证结果:")
for data in test_data:
email_valid = validate_email(data)
phone_valid = validate_phone(data)
id_valid = validate_id_card(data)
print(f" '{data}': 邮箱({email_valid}), 手机({phone_valid}), 身份证({id_valid})")
data_validation()
4.2 文本数据提取
python
import re
# 文本数据提取
def text_extraction():
# 复杂的文本数据
complex_text = """
订单信息:
订单号: ORD20240115001, 金额: ¥1,250.50, 状态: 已完成
客户: 张三, 电话: 138-1234-5678, 邮箱: zhangsan@email.com
地址: 北京市朝阳区xxx街道100号
商品清单:
1. 笔记本电脑 - 数量: 1 - 单价: ¥8,999.00
2. 鼠标 - 数量: 2 - 单价: ¥150.00
3. 键盘 - 数量: 1 - 单价: ¥450.00
"""
# 提取订单基本信息
order_info = {}
# 提取订单号
order_match = re.search(r'订单号:\s*(\w+)', complex_text)
if order_match:
order_info['order_no'] = order_match.group(1)
# 提取金额(处理千分位分隔符)
amount_match = re.search(r'金额:\s*¥([\d,]+\.\d+)', complex_text)
if amount_match:
amount = amount_match.group(1).replace(',', '')
order_info['amount'] = float(amount)
# 提取客户信息
customer_match = re.search(r'客户:\s*(\w+)', complex_text)
if customer_match:
order_info['customer'] = customer_match.group(1)
# 提取商品信息
products = []
product_pattern = r'(\d+)\.\s*([^-]+)\s*-\s*数量:\s*(\d+)\s*-\s*单价:\s*¥([\d,]+\.\d+)'
for match in re.finditer(product_pattern, complex_text):
product = {
'seq': match.group(1),
'name': match.group(2).strip(),
'quantity': int(match.group(3)),
'price': float(match.group(4).replace(',', ''))
}
products.append(product)
order_info['products'] = products
print("提取的订单信息:")
for key, value in order_info.items():
if key != 'products':
print(f" {key}: {value}")
else:
print(" 商品清单:")
for product in value:
print(f" {product['seq']}. {product['name']} - {product['quantity']}件 - ¥{product['price']}")
text_extraction()
4.3 日志分析
python
import re
from collections import Counter
# 日志分析
def log_analysis():
# 模拟日志数据
logs = """
2024-01-15 08:30:15 [INFO] User login successful: user123
2024-01-15 08:35:22 [ERROR] Database connection failed
2024-01-15 08:40:10 [WARN] High memory usage detected
2024-01-15 09:15:30 [INFO] File upload completed: file.pdf
2024-01-15 09:20:45 [ERROR] Permission denied for user: user456
2024-01-15 10:05:18 [INFO] User logout: user123
2024-01-15 10:30:55 [ERROR] Invalid API request
"""
# 分析日志级别分布
level_pattern = r'\[(\w+)\]'
levels = re.findall(level_pattern, logs)
level_count = Counter(levels)
print("日志级别统计:")
for level, count in level_count.items():
print(f" {level}: {count}次")
# 提取错误日志的详细信息
error_pattern = r'\[ERROR\]\s*(.+)'
errors = re.findall(error_pattern, logs)
print("
错误日志详情:")
for i, error in enumerate(errors, 1):
print(f" {i}. {error}")
# 提取用户操作
user_actions = re.findall(r'User\s+(\w+):\s*(\w+)', logs)
print("
用户操作记录:")
for action, user in user_actions:
print(f" 用户 {user} 执行了 {action} 操作")
log_analysis()
五、性能优化与最佳实践
5.1 性能优化技巧
python
import re
import time
def performance_tips():
# 1. 使用原始字符串
good_pattern = r'\d+\.\d+' # 推荐
bad_pattern = '\\d+\\.\\d+' # 不推荐
# 2. 避免回溯灾难
text = "aaaaaaaaaaaaaaaaaaaaab"
# 糟糕的模式 - catastrophic backtracking
start_time = time.time()
try:
bad_match = re.search(r'(a+)+b', text)
bad_time = time.time() - start_time
except:
bad_time = float('inf')
# 优化的模式
start_time = time.time()
good_match = re.search(r'a+b', text)
good_time = time.time() - start_time
print(f"糟糕模式耗时: {bad_time:.6f}秒")
print(f"优化模式耗时: {good_time:.6f}秒")
# 3. 使用适当的量词
text2 = "<div>content</div>" * 1000
# 贪婪量词
start_time = time.time()
greedy_result = re.findall(r'<div>.*</div>', text2)
greedy_time = time.time() - start_time
# 非贪婪量词
start_time = time.time()
lazy_result = re.findall(r'<div>.*?</div>', text2)
lazy_time = time.time() - start_time
print(f"贪婪量词匹配{len(greedy_result)}次,耗时: {greedy_time:.6f}秒")
print(f"非贪婪量词匹配{len(lazy_result)}次,耗时: {lazy_time:.6f}秒")
performance_tips()
5.2 错误处理与调试
python
import re
def error_handling():
def safe_re_search(pattern, text, flags=0):
try:
compiled = re.compile(pattern, flags)
return compiled.search(text)
except re.error as e:
print(f"正则表达式错误: {e}")
return None
# 测试错误处理
test_cases = [
(r'valid_pattern\d+', 'text123'),
(r'invalid[pattern', 'text'), # 未闭合字符集
(r'*invalid', 'text'), # 量词使用错误
]
for pattern, text in test_cases:
print(f"
测试模式: {pattern}")
result = safe_re_search(pattern, text)
if result:
print(f" 匹配成功: {result.group()}")
else:
print(" 匹配失败或模式错误")
error_handling()
正则表达式是Python文本处理的利器,通过熟练掌握re模块的各种函数和正则表达式语法,可以高效解决复杂的字符串处理问题。在实际应用中,建议根据具体需求选择合适的函数,注意性能优化,并妥善处理可能的异常情况。