文章目录

Python 正则表达式:re 模块与匹配模式

发布于 2026-04-05 08:38:02 · 浏览 17 次 · 评论 0 条

Python 正则表达式:re 模块与匹配模式

正则表达式是处理文本的利器。无论你需要验证用户输入、提取网页数据,还是在日志中查找特定信息,正则表达式都能帮你用简洁的代码完成复杂任务。本文将系统讲解 Python 中 re 模块的使用方法,以及各种匹配模式的配置技巧。


一、为什么需要正则表达式

假设你有一串文本 "用户张三的电话是13800138000,邮箱是zhangsan@example.com"。如果你想提取其中的手机号和邮箱地址,传统的字符串处理方式需要多次调用 find()split() 等方法,代码冗长且易出错。

而使用正则表达式,你只需编写一条模式规则:

import re

text = "用户张三的电话是13800138000,邮箱是zhangsan@example.com"

# 一行代码提取手机号
phone = re.search(r'\d{11}', text)
print(phone.group())  # 输出:13800138000

# 一行代码提取邮箱
email = re.search(r'\w+@\w+\.\w+', text)
print(email.group())  # 输出:zhangsan@example.com

这就是正则表达式的威力——用精炼的语法描述文本模式,实现高效的匹配与提取。


二、re 模块的核心函数

Python 的 re 模块提供了多个函数来处理正则表达式。理解这些函数的适用场景,是灵活运用正则的第一步。

2.1 搜索与匹配的区别

re.search() 会在整个字符串中搜索第一个匹配项,re.match() 只在字符串开头进行匹配。

import re

text = "Python编程指南"

# search 在整个字符串中查找
result = re.search(r'编程', text)
print(result.group())  # 输出:编程

# match 只在开头查找
result = re.match(r'Python', text)
print(result.group())  # 输出:Python

result = re.match(r'编程', text)
print(result)  # 输出:None,因为"编程"不在开头

2.2 提取所有匹配项

当需要提取字符串中所有符合条件的内容时,使用 re.findall()

import re

text = "苹果3个,香蕉2个,橘子5个,葡萄8个"

# 提取所有数字
numbers = re.findall(r'\d+', text)
print(numbers)  # 输出:['3', '2', '5', '8']

如果匹配结果包含多个分组,findall() 会以元组形式返回所有分组。

text = "2024-01-15 和 2025-02-20"

# 分组提取年、月、日
dates = re.findall(r'(\d{4})-(\d{2})-(\d{2})', text)
print(dates)  # 输出:[('2024', '01', '15'), ('2025', '02', '20')]

2.3 替换与分割

re.sub() 用于替换匹配的文本,功能类似于字符串的 replace() 方法,但支持更复杂的匹配规则。

import re

text = "原价100元,现在只要99元!"

# 将所有数字替换为[数字]
result = re.sub(r'\d+', r'[\g<0>]', text)
print(result)  # 输出:原价[100]元,现在只要[99]元!

re.split() 支持按正则模式进行分割,比字符串的 split() 更灵活。

text = "苹果,香蕉;橘子|葡萄"

# 按逗号、分号或竖线分割
result = re.split(r'[;,|]', text)
print(result)  # 输出:['苹果', '香蕉', '橘子', '葡萄']

三、匹配模式详解

re 模块的函数接受一个 flags 参数,用于控制匹配的行为方式。合理使用匹配模式可以让代码更简洁、更强大。

3.1 常用匹配模式一览

模式 常量 说明
不区分大小写 re.IGNORECASEre.I Aa 被视为相同
多行模式 re.MULTILINEre.M ^$` 匹配每行开头和结尾 | | 点号匹配所有 | `re.DOTALL` 或 `re.S` | `.` 可以匹配换行符 `\n` | | 详细模式 | `re.VERBOSE` | 允许在正则中添加注释和空格 | | Unicode 匹配 | `re.UNICODE` 或 `re.U` | 按 Unicode 规则匹配(Python 3 默认) | ### 3.2 不区分大小写模式 当你需要匹配英文文本,但不确定大小写时,使用 `re.IGNORECASE`。 ```python import re text = "Python PYTHON python" # 不区分大小写匹配 result = re.findall(r'python', text, re.IGNORECASE) print(result) # 输出:['Python', 'PYTHON', 'python'] ``` ### 3.3 多行模式 默认情况下,`^` 只匹配整个字符串的开头,`$ 只匹配整个字符串的结尾。开启多行模式后,它们会匹配每一行的开头和结尾。
import re

text = """第一行内容
第二行内容
第三行内容"""

# 开启多行模式,^ 匹配每行开头
result = re.findall(r'^第二行', text, re.MULTILINE)
print(result)  # 输出:['第二行']

# 不开启多行模式,^ 只匹配整个字符串开头
result = re.findall(r'^第二行', text)
print(result)  # 输出:[]

3.4 点号匹配所有

点号 . 默认不匹配换行符。开启 re.DOTALL 后,点号可以匹配包括换行符在内的任意字符。

import re

text = """第一行
第二行"""

# 默认情况下,. 不匹配换行符
result = re.findall(r'第一行.第二行', text)
print(result)  # 输出:[]

# 开启 DOTALL 模式
result = re.findall(r'第一行.第二行', text, re.DOTALL)
print(result)  # 输出:['第一行\n第二行']

3.5 详细模式

当正则表达式变得复杂时,使用 re.VERBOSE 可以在模式中添加注释和空格,提高可读性。

import re

text = "联系方式:13800138000,邮箱:test@example.com"

# 未使用详细模式
pattern1 = r'联系方式:(\d{11}),邮箱:(\w+@\w+\.\w+)'

# 使用详细模式,便于阅读和维护
pattern2 = r"""
联系方式:        # 匹配固定前缀
(\d{11})         # 匹配11位手机号
,邮箱:          # 匹配分隔符
(\w+@\w+\.\w+)   # 匹配邮箱格式
"""

result = re.search(pattern2, text, re.VERBOSE)
print(result.group(1))  # 输出:13800138000
print(result.group(2))  # 输出:test@example.com

四、编译正则表达式

如果同一个正则表达式需要多次使用,建议先使用 re.compile() 编译它,这样可以提升执行效率。

import re

# 编译一个匹配手机号的正则
phone_pattern = re.compile(r'\d{11}')

text1 = "张三的手机是13800138000"
text2 = "李四的手机是13900139000"

# 直接调用编译后的对象方法
result1 = phone_pattern.search(text1)
print(result1.group())  # 输出:13800138000

result2 = phone_pattern.search(text2)
print(result2.group())  # 输出:13900139000

编译时同样可以指定匹配模式:

# 编译一个不区分大小写的单词匹配模式
word_pattern = re.compile(r'python', re.IGNORECASE)

五、常用正则语法速查

掌握正则的核心语法,才能写出准确的匹配模式。

5.1 字符类

import re

# [abc] 匹配任意一个字符
re.findall(r'[aeiou]', "hello")  # 输出:['e', 'o']

# [a-z] 匹配任意小写字母
re.findall(r'[a-z]', "Hello")  # 输出:['e', 'llo']

# [^abc] 匹配除 a、b、c 之外的任意字符
re.findall(r'[^aeiou]', "hello")  # 输出:['h', 'l', 'l']

5.2 预定义字符

符号 含义
\d 任意数字,相当于 [0-9]
\D 任意非数字,相当于 [^0-9]
\w 任意字母、数字或下划线
\W 任意非字母、数字、下划线
\s 任意空白字符(空格、制表符、换行符)
\S 任意非空白字符
. 任意字符(除换行符)

5.3 量词

符号 含义
* 零次或多次
+ 一次或多次
? 零次或一次
{n} 恰好 n 次
{n,} 至少 n 次
{n,m} 至少 n 次,至多 m 次
import re

text = "color colour colooour"

# 匹配 "color" 或 "colour"(u 可有可无)
re.findall(r'colou?r', text)  # 输出:['color', 'colour']

# 匹配至少 2 个 o
re.findall(r'co{2,}r', text)  # 输出:['colooour']

5.4 锚点与边界

import re

text = "apple Apple APPLE"

# ^ 匹配字符串开头
re.findall(r'^apple', text)  # 输出:['apple']

# $ 匹配字符串结尾
re.findall(r'APPLE$', text)  # 输出:['APPLE']

# \b 匹配单词边界
re.findall(r'\bapple\b', text)  # 输出:['apple']

5.5 分组与捕获

使用圆括号 () 创建分组,既可以限定量词的作用范围,也可以捕获匹配的子内容。

import re

text = "2024-05-15"

# 分组提取年月日
pattern = r'(\d{4})-(\d{2})-(\d{2})'
match = re.search(pattern, text)
print(match.group(0))  # 完整匹配:2024-05-15
print(match.group(1))  # 第一个分组:2024
print(match.group(2))  # 第二个分组:05
print(match.group(3))  # 第三个分组:15

# 使用分组名称
pattern_named = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
match = re.search(pattern_named, text)
print(match.group('year'))   # 输出:2024
print(match.group('month'))  # 输出:05

六、综合示例

以下示例演示如何组合使用 re 模块的各种功能,解决实际文本处理问题。

import re

# 示例文本
log_text = """
[2024-05-15 10:30:45] INFO: 用户登录成功,用户ID: user_123
[2024-05-15 10:31:02] ERROR: 数据库连接失败,错误码: DB_CONN_001
[2024-05-15 10:32:18] WARNING: 磁盘空间不足,使用率: 85%
[2024-05-15 10:33:00] INFO: 用户user_456完成交易
"""

# 1. 提取所有日志级别
levels = re.findall(r']\] (\w+):', log_text)
print("日志级别:", levels)  # 输出:['INFO', 'ERROR', 'WARNING', 'INFO']

# 2. 提取所有用户ID
users = re.findall(r'用户[ID]?:?\s*(\w+)', log_text)
print("用户ID:", users)  # 输出:['user_123', 'user_456']

# 3. 提取时间和用户(分组使用)
pattern = r'\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*?用户ID: (\w+)'
matches = re.findall(pattern, log_text)
print("时间和用户:", matches)
# 输出:[('2024-05-15 10:30:45', 'user_123')]

# 4. 替换所有用户ID为 [用户已隐藏]
hidden = re.sub(r'user_\d+', '[用户已隐藏]', log_text)
print(hidden)

# 5. 验证输入是否为有效邮箱
def is_valid_email(email):
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None

print(is_valid_email("test@example.com"))  # 输出:True
print(is_valid_email("invalid-email"))     # 输出:False

七、常见问题与注意事项

转义字符的处理

正则表达式中的特殊字符(如 .*? 等)需要转义。使用原始字符串 r'' 前缀可以避免双重转义的困扰。

import re

# 匹配点号,需要转义
text = "版本号:1.0.2"
result = re.search(r'1\.0\.2', text)
print(result.group())  # 输出:1.0.2

贪婪与非贪婪匹配

默认情况下,量词采用贪婪模式,会匹配尽可能多的字符。在量词后加上 ? 可以切换为非贪婪模式,匹配尽可能少的字符。

import re

html = "<div>内容一</div><div>内容二</div>"

# 贪婪模式:匹配到最后一个 </div>
match1 = re.search(r'<div>.*</div>', html)
print(match1.group())  # 输出:<div>内容一</div><div>内容二</div>

# 非贪婪模式:匹配到第一个 </div>
match2 = re.search(r'<div>.*?</div>', html)
print(match2.group())  # 输出:<div>内容一</div>

性能优化建议

对于需要多次使用的正则表达式,务必先编译。如果匹配规则固定不变,编译一次可以避免每次匹配时的重复解析开销。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文