Python3 正则表达式之正则表达式模式详解
Python3 正则表达式之正则表达式模式详解
🎯 学习目标
深入理解 Python 正则表达式中的各种模式匹配语法,掌握元字符、量词、分组、断言等高级用法。能够在复杂文本处理任务中编写高效、准确的正则表达式,并具备调试和优化正则表达式的能力。
🔑 核心重点
分类 | 内容 |
---|---|
元字符 | . 、^ 、` |
-------------- | ------------------------------------------------------------ |
、\d 、\w 、\s 等 |
|
量词 | * 、+ 、? 、{n,m} 控制重复次数 |
分组与捕获 | ( ) 捕获分组、(?: ) 非捕获分组、(?P<name>...) 命名分组 |
断言 | (?=...) 正向肯定查找、(?!...) 正向否定查找 |
贪婪 vs 非贪婪 | 默认贪婪匹配,使用 ? 变为非贪婪 |
编译模式 | 使用 re.compile() 提高效率,支持标志位(如 re.IGNORECASE ) |
📚 详细讲解
一、元字符:基础匹配单位
元字符用于表示某一类字符或特殊含义。
元字符 | 含义 | 示例 |
---|---|---|
. |
匹配任意单个字符(换行除外) | r'.at' 匹配 cat、hat、bat 等 |
\d |
数字 [0-9] | r'\d+' 匹配连续数字 |
\D |
非数字 | r'\D+' 匹配非数字字符串 |
\w |
单词字符 [a-zA-Z0-9_] | r'\w+' 匹配标识符、变量名等 |
\W |
非单词字符 | r'\W+' 匹配标点、空格等 |
\s |
空白字符(空格、tab、换行) | r'\s+' 匹配任意空白 |
\S |
非空白字符 | r'\S+' 匹配非空白内容 |
\b |
单词边界 | r'\bcat\b' 匹配完整单词 "cat" |
\B |
非单词边界 | r'\Bcat\B' 匹配 "category" 中的 cat |
✅ 示例:
import re
text = "abc123xyz"
pattern = r'\d+' # 匹配所有数字
print(re.findall(pattern, text)) # 输出: ['123']
二、量词:控制重复次数
量词 | 含义 | 示例 |
---|---|---|
* |
0 次或多次 | r'a*' 匹配 ""、"a"、"aa" 等 |
+ |
至少 1 次 | r'\d+' 匹配至少一个数字 |
? |
0 或 1 次 | r'file-?\d+' 匹配 file123 或 file-123 |
{n} |
精确 n 次 | r'\d{3}' 匹配三位数字 |
{n,} |
至少 n 次 | r'\w{5,}' 匹配长度 ≥5 的单词 |
{n,m} |
n 到 m 次 | r'[A-Z]{2,4}' 匹配 2~4 个大写字母 |
✅ 示例:
text = "abc a123 defg"
pattern = r'\w{3,5}' # 匹配长度为 3~5 的单词
print(re.findall(pattern, text)) # 输出: ['abc', 'a123', 'defg']
三、分组与命名捕获
分组形式 | 用途说明 |
---|---|
(pattern) |
捕获分组,结果会保存在 group() 中 |
(?:pattern) |
非捕获分组,不保存匹配结果 |
(?P<name>...) |
命名捕获分组,可通过名称访问匹配内容 |
(?P=name) |
引用之前定义的命名组 |
✅ 示例:
text = "John Doe <john@example.com>"
pattern = r'(?P<name>\w+) (?P<lastname>\w+) <(?P<email>[^>]+)>'
match = re.search(pattern, text)
if match:
print("Name:", match.group('name')) # John
print("Email:", match.group('email')) # john@example.com
四、断言:条件匹配
表达式 | 含义 |
---|---|
(?=pattern) |
正向肯定预查:后面是 pattern |
(?!pattern) |
正向否定预查:后面不是 pattern |
(?<=pattern) |
反向肯定预查:前面是 pattern |
(?<!pattern) |
反向否定预查:前面不是 pattern |
✅ 示例:提取价格前带美元符号的数字
text = "$100, $200, ¥300"
pattern = r'\$(?=\d+)'
matches = re.findall(pattern, text)
print(matches) # 输出: ['$'] (只匹配 $ 后面有数字的情况)
五、贪婪 vs 非贪婪匹配
默认情况下,正则表达式是“贪婪匹配”,即尽可能多地匹配内容。使用 ?
可以变为“非贪婪匹配”。
✅ 示例:
text = "<div>Hello</div><span>World</span>"
# 贪婪匹配
pattern_greedy = r'<.*>'
print(re.findall(pattern_greedy, text))
# 输出: ['<div>Hello</div><span>World</span>']
# 非贪婪匹配
pattern_lazy = r'<.*?>'
print(re.findall(pattern_lazy, text))
# 输出: ['<div>', '</div>', '<span>', '</span>']
六、编译模式与标志位
标志位 | 功能说明 |
---|---|
re.IGNORECASE (I ) |
忽略大小写匹配 |
re.MULTILINE (M ) |
多行模式,^ 和 ` |
--------------------- | --------------------------------------- |
re.IGNORECASE (I ) |
忽略大小写匹配 |
匹配每行开头和结尾 | |
re.DOTALL (S ) |
. 匹配包括换行在内的所有字符 |
re.VERBOSE (X ) |
支持注释和空白,便于阅读长正则 |
✅ 示例:
text = "Hello\nWorld"
pattern = re.compile(r'^world', re.IGNORECASE | re.MULTILINE)
matches = pattern.findall(text)
print(matches) # 输出: ['World']
⚠️ 注意事项
- 尽量避免使用过于复杂的正则表达式,影响可读性和性能。
- 使用
re.escape()
对用户输入进行转义,防止正则注入攻击。 - 在 PyCharm 中可以使用
Find in Path
+Regex
模式快速测试正则。 - 正则表达式匹配失败时返回
None
,务必做判断处理。 - 使用命名分组提升代码可读性,尤其在提取结构化数据时。
🧪 实际案例分析
📌 场景:提取 HTML 中所有的超链接和锚文本
示例代码:
import re
html = '''
<p>这是第一个链接:<a href="https://example.com">示例网站</a></p>
<p>另一个链接:<a href="http://test.org">测试站点</a></p>
'''
# 匹配 <a> 标签并提取 href 和锚文本
pattern = r'<a\s+href=["\'](.*?)["\'].*?>(.*?)</a>'
links = re.findall(pattern, html, re.DOTALL)
for href, text in links:
print(f"链接: {href}, 文本: {text.strip()}")
📌 输出:
链接: https://example.com, 文本: 示例网站
链接: http://test.org, 文本: 测试站点
📌 说明:
- 使用了非贪婪匹配
.*?
来精确提取内容。 - 使用
re.DOTALL
让.
匹配换行符,适用于多行 HTML。 - 使用分组分别提取
href
和锚文本。
🧩 拓展练习(动手实践)
- 编写正则表达式提取一段文本中所有合法的电子邮件地址。
- 编写程序识别一段文本中的 IP 地址(IPv4),并验证其格式是否正确。
- 编写脚本从一段日志中提取 HTTP 请求状态码(如 200、404、500)并统计出现频率。
- 编写函数将一段中文文本中的日期格式统一转换为
YYYY-MM-DD
格式(如“2025年6月3日” → “2025-06-03”)。 - 编写程序从 JSON 字符串中提取所有的键名(key)。
如果你希望我为你提供:
- 《Python 正则表达式模式速查表 PDF》
- 更多实战项目练习题(含解析)
- 视频教学资源推荐(中文讲解)
- 如何结合 PyCharm 快捷键快速调试正则表达式
欢迎随时告诉我 😊