Python正则表达式详解
1. 什么是正则表达式
正则表达式(Regular Expression,简称regex或regexp)是一种用于匹配字符串的模式。它可以用来:
- 验证输入格式(如邮箱、手机号等)
- 提取特定格式的文本
- 替换文本内容
- 分割字符串
2. Python中的re模块
Python内置了re模块来处理正则表达式:
import re
2.1 re模块主要函数
| 函数 | 功能 | 返回值 |
|---|---|---|
|
|
从字符串开头匹配 |
Match对象或None |
|
|
搜索整个字符串 |
Match对象或None |
|
|
找到所有匹配项 |
列表 |
|
|
找到所有匹配项(迭代器) |
迭代器 |
|
|
替换匹配项 |
字符串 |
|
|
分割字符串 |
列表 |
3. 正则表达式基础语法
3.1 基本字符匹配
import re
# 直接匹配字符
pattern = r"hello"
text = "hello world"
result = re.match(pattern, text)
print(result.group()) # 输出: hello
3.2 特殊字符(元字符)
| 字符 | 含义 | 示例 |
|---|---|---|
|
|
匹配任意字符(除换行符) |
|
|
|
匹配字符串开头 |
|
|
|
匹配字符串结尾 |
|
|
|
匹配前一个字符0次或多次 |
|
|
|
匹配前一个字符1次或多次 |
|
|
|
匹配前一个字符0次或1次 |
|
|
|
匹配前一个字符n次 |
|
|
|
匹配前一个字符至少n次 |
|
|
|
匹配前一个字符n到m次 |
|
3.3 字符类
| 字符类 | 含义 | 示例 |
|---|---|---|
|
|
匹配方括号中的任意一个字符 |
|
|
|
匹配除方括号中字符外的任意字符 |
|
|
|
匹配小写字母 |
|
|
|
匹配大写字母 |
|
|
|
匹配数字 |
|
3.4 预定义字符类
| 字符类 | 含义 | 等价写法 |
|---|---|---|
|
|
数字字符 |
|
|
|
非数字字符 |
|
|
|
单词字符(字母、数字、下划线) |
|
|
|
非单词字符 |
|
|
|
空白字符(空格、制表符、换行符等) |
|
|
|
非空白字符 |
|
3.5 转义字符
当需要匹配特殊字符本身时,需要使用反斜杠转义:
# 匹配点号
pattern = r"\."
text = "file.txt"
result = re.search(pattern, text)
print(result.group()) # 输出: .
4. 常用re函数详解
4.1 re.match() - 从开头匹配
import re
# match只从字符串开头开始匹配
text = "hello world"
pattern = r"hello"
result = re.match(pattern, text)
if result:
print("匹配成功:", result.group()) # 输出: 匹配成功: hello
else:
print("匹配失败")
# 如果不从开头匹配,则返回None
text2 = "say hello world"
result2 = re.match(pattern, text2)
print(result2) # 输出: None
4.2 re.search() - 全局搜索
import re
# search搜索整个字符串
text = "say hello world"
pattern = r"hello"
result = re.search(pattern, text)
if result:
print("找到匹配:", result.group()) # 输出: 找到匹配: hello
print("匹配位置:", result.span()) # 输出: 匹配位置: (4, 9)
4.3 re.findall() - 找到所有匹配
import re
# findall返回所有匹配项组成的列表
text = "phone: 138-1234-5678, mobile: 159-8765-4321"
pattern = r"\d{3}-\d{4}-\d{4}"
matches = re.findall(pattern, text)
print(matches) # 输出: ['138-1234-5678', '159-8765-4321']
4.4 re.finditer() - 返回迭代器
import re
# finditer返回Match对象的迭代器,节省内存
text = "phone: 138-1234-5678, mobile: 159-8765-4321"
pattern = r"\d{3}-\d{4}-\d{4}"
for match in re.finditer(pattern, text):
print(f"匹配内容: {match.group()}, 位置: {match.span()}")
4.5 re.sub() - 替换匹配项
import re
# sub用于替换匹配的文本
text = "我的电话是138-1234-5678"
pattern = r"\d{3}-\d{4}-\d{4}"
replacement = "****-****-****"
new_text = re.sub(pattern, replacement, text)
print(new_text) # 输出: 我的电话是****-****-****
# 使用函数进行复杂替换
def mask_phone(match):
phone = match.group()
return phone[:3] + "****" + phone[-4:]
text2 = "联系我: 138-1234-5678 或 159-8765-4321"
result = re.sub(pattern, mask_phone, text2)
print(result) # 输出: 联系我: 138****5678 或 159****4321
4.6 re.split() - 分割字符串
import re
# split使用正则表达式分割字符串
text = "apple,banana;orange:grape"
pattern = r"[,;:]"
fruits = re.split(pattern, text)
print(fruits) # 输出: ['apple', 'banana', 'orange', 'grape']
# 带捕获组的分割
text2 = "apple123banana456orange"
pattern2 = r"(\d+)"
result = re.split(pattern2, text2)
print(result) # 输出: ['apple', '123', 'banana', '456', 'orange']
5. 高级特性
5.1 分组和捕获
import re
# 使用括号创建分组
text = "John Doe, age 30"
pattern = r"(\w+)\s+(\w+),\s+age\s+(\d+)"
match = re.search(pattern, text)
if match:
print("完整匹配:", match.group(0)) # 输出: John Doe, age 30
print("第一个分组:", match.group(1)) # 输出: John
print("第二个分组:", match.group(2)) # 输出: Doe
print("第三个分组:", match.group(3)) # 输出: 30
print("所有分组:", match.groups()) # 输出: ('John', 'Doe', '30')
5.2 命名分组
import re
# 使用命名分组,提高代码可读性
text = "John Doe, age 30"
pattern = r"(?P<first_name>\w+)\s+(?P<last_name>\w+),\s+age\s+(?P<age>\d+)"
match = re.search(pattern, text)
if match:
print("姓名:", match.group('first_name'), match.group('last_name'))
print("年龄:", match.group('age'))
print("所有组:", match.groupdict())
5.3 编译正则表达式
import re
# 预编译正则表达式提高性能
pattern = re.compile(r"\d{3}-\d{4}-\d{4}")
text1 = "phone: 138-1234-5678"
text2 = "mobile: 159-8765-4321"
# 使用编译后的模式
result1 = pattern.search(text1)
result2 = pattern.search(text2)
print(result1.group()) # 输出: 138-1234-5678
print(result2.group()) # 输出: 159-8765-4321
5.4 标志参数
import re
# re.IGNORECASE - 忽略大小写
text = "Hello World"
pattern = r"hello"
result = re.search(pattern, text, re.IGNORECASE)
print(result.group()) # 输出: Hello
# re.MULTILINE - 多行模式
text = """first line
second line
third line"""
pattern = r"^second"
# 不使用MULTILINE标志,匹配失败
result1 = re.search(pattern, text)
print(result1) # 输出: None
# 使用MULTILINE标志
result2 = re.search(pattern, text, re.MULTILINE)
print(result2.group()) # 输出: second
# re.DOTALL - 让.匹配包括换行符在内的所有字符
text = "hello\nworld"
pattern = r"hello.world"
result1 = re.search(pattern, text) # 不匹配
result2 = re.search(pattern, text, re.DOTALL) # 匹配
print(result2.group()) # 输出: hello
# world
6. 实战应用示例
6.1 邮箱验证
import re
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
# 测试
emails = [
"user@example.com",
"test.email+tag@domain.co.uk",
"invalid.email",
"user@domain"
]
for email in emails:
print(f"{email}: {'有效' if validate_email(email) else '无效'}")
6.2 手机号验证
import re
def validate_phone(phone):
# 中国手机号验证
pattern = r'^1[3-9]\d{9}$'
return bool(re.match(pattern, phone))
phones = ["13812345678", "15987654321", "12345678901", "1381234567"]
for phone in phones:
print(f"{phone}: {'有效' if validate_phone(phone) else '无效'}")
6.3 提取HTML标签
import re
html = '<div class="container"><p>Hello <strong>World</strong></p></div>'
# 提取所有标签名
tag_pattern = r'<(\w+)'
tags = re.findall(tag_pattern, html)
print("标签:", tags) # 输出: ['div', 'p', 'strong']
# 提取标签内容
content_pattern = r'<[^>]*>([^<]*)</[^>]*>'
contents = re.findall(content_pattern, html)
print("内容:", contents) # 输出: ['Hello ', 'World']
6.4 日志分析
import re
log_line = '192.168.1.1 - - [23/Nov/2025:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234'
# 解析Apache日志格式
log_pattern = r'(\d+\.\d+\.\d+\.\d+) - - \[([^\]]+)\] "(\w+) ([^"]+)" (\d+) (\d+)'
match = re.match(log_pattern, log_line)
if match:
ip, timestamp, method, url, status, size = match.groups()
print(f"IP: {ip}")
print(f"时间: {timestamp}")
print(f"方法: {method}")
print(f"URL: {url}")
print(f"状态码: {status}")
print(f"大小: {size}")
7. 贪婪与非贪婪匹配
7.1 基础概念对比
| 对比项 | 贪婪匹配 | 非贪婪匹配 |
|---|---|---|
|
定义 |
默认行为,尽可能多地匹配字符 |
在量词后加 |
|
匹配策略 |
贪得无厌,匹配到最长可能的字符串 |
适可而止,匹配到最短必需的字符串 |
|
符号表示 |
|
|
|
优先级 |
高(默认) |
需要显式指定 |
7.2 量词对比表
| 量词类型 | 贪婪形式 | 非贪婪形式 | 含义 |
|---|---|---|---|
|
零次或多次 |
|
|
匹配0个或更多,尽可能多/少 |
|
一次或多次 |
|
|
匹配1个或更多,尽可能多/少 |
|
零次或一次 |
|
|
匹配0个或1个,尽可能多/少 |
|
确切次数 |
|
|
匹配n到m次,尽可能多/少 |
7.3 实际示例对比
| 示例文本 | 模式 | 贪婪匹配结果 | 非贪婪匹配结果 | 说明 |
|---|---|---|---|---|
|
|
|
|
|
贪婪匹配到最远数字,非贪婪匹配到最近数字 |
|
|
|
|
|
贪婪匹配整个范围,非贪婪匹配单个标签 |
|
|
|
|
|
贪婪匹配跨越引号,非贪婪分别匹配 |
|
|
|
|
|
贪婪匹配全部,非贪婪逐个匹配 |
|
|
|
|
|
贪婪匹配非空,非贪婪匹配空串 |
7.4 应用场景对比
| 场景 | 推荐匹配方式 | 原因 |
|---|---|---|
|
提取HTML标签 |
非贪婪 ( |
避免匹配整个文档 |
|
提取引号内容 |
非贪婪 ( |
避免跨越多个引号对 |
|
验证格式 |
贪婪 |
通常格式验证不需要精确控制 |
|
搜索关键词 |
贪婪 |
通常需要完整匹配 |
|
日志解析 |
非贪婪 |
避免跨越日志条目边界 |
|
数字提取 |
贪婪 |
通常需要完整数字串 |
7.5 问题示例与解决方案
import re
text = "<div>content</div><span>text</span>"
# 贪婪匹配(默认)
greedy = re.findall(r'<.*>', text)
print(greedy) # ['<div>content</div><span>text</span>']
# 非贪婪匹配
non_greedy = re.findall(r'<.*?>', text)
print(non_greedy) # ['<div>', '</div>', '<span>', '</span>']
更多推荐
所有评论(0)