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 和锚文本。

🧩 拓展练习(动手实践)

  1. 编写正则表达式提取一段文本中所有合法的电子邮件地址。
  2. 编写程序识别一段文本中的 IP 地址(IPv4),并验证其格式是否正确。
  3. 编写脚本从一段日志中提取 HTTP 请求状态码(如 200、404、500)并统计出现频率。
  4. 编写函数将一段中文文本中的日期格式统一转换为 YYYY-MM-DD 格式(如“2025年6月3日” → “2025-06-03”)。
  5. 编写程序从 JSON 字符串中提取所有的键名(key)。

如果你希望我为你提供:

  • 《Python 正则表达式模式速查表 PDF》
  • 更多实战项目练习题(含解析)
  • 视频教学资源推荐(中文讲解)
  • 如何结合 PyCharm 快捷键快速调试正则表达式

欢迎随时告诉我 😊

posted @ 2025-06-03 08:27  红尘过客2022  阅读(78)  评论(0)    收藏  举报