共计 1553 个字符,预计需要花费 4 分钟才能阅读完成。
在前一节中,我们学习了正则表达式的零宽断言,掌握了如何匹配位置而非内容。今天我们来探讨另一个常见且重要的主题——贪婪与非贪婪匹配。理解这两者的区别,是编写高效正则表达式的关键。
一、什么是贪婪匹配
在正则表达式中,大多数字符量词(如 *、+、?、{m,n})默认都是 贪婪模式。
所谓贪婪,是指匹配时尽可能多地匹配字符。
举个例子:
import re
text = "<p>Python</p><p>Regex</p>"
pattern = r"<p>.*</p>"
result = re.findall(pattern, text)
print(result)
输出结果为:
['<p>Python</p><p>Regex</p>']
解释:
.* 是贪婪的,会尽可能多地匹配字符,因此从第一个 <p> 一直匹配到最后一个 </p>。
二、非贪婪匹配(惰性匹配)
如果希望匹配尽可能少的内容,可以在量词后加上一个 ?,表示 非贪婪模式。
修改上面的例子:
pattern = r"<p>.*?</p>"
result = re.findall(pattern, text)
print(result)
输出结果为:
['<p>Python</p>', '<p>Regex</p>']
解释:
.*? 表示“匹配尽可能少的字符”,因此在第一个 </p> 就停止匹配。
三、常见的贪婪与非贪婪量词对比表
| 贪婪量词 | 非贪婪量词 | 含义 |
|---|---|---|
* |
*? |
匹配前面的子表达式零次或多次 |
+ |
+? |
匹配前面的子表达式一次或多次 |
? |
?? |
匹配前面的子表达式零次或一次 |
{m,n} |
{m,n}? |
匹配前面的子表达式 m 到 n 次之间 |
四、更多示例讲解
- 匹配 HTML 标签内容
text = "<div>Apple</div><div>Orange</div>"
pattern = r"<div>.*?</div>"
print(re.findall(pattern, text))
结果为:
['<div>Apple</div>', '<div>Orange</div>']
如果不加 ?,则会一次性匹配整个字符串。
- 提取引号中的内容
text = 'name="Tom" age="25" city="Beijing"'
pattern = r'".*?"'
print(re.findall(pattern, text))
输出结果:
['"Tom"', '"25"', '"Beijing"']
如果使用贪婪模式 ".*",则会得到:
['"Tom" age="25" city="Beijing"']
明显不是我们想要的结果。
五、混合使用实例
贪婪与非贪婪模式可以结合使用,用于控制不同部分的匹配行为。
text = "id=123 name=Tom id=456 name=Jerry"
pattern = r"id=\d+ name=.*? "
print(re.findall(pattern, text))
输出结果:
['id=123 name=Tom ', 'id=456 name=Jerry ']
这里 \d+ 使用贪婪模式,.*? 使用非贪婪模式,实现了精准提取。
六、应用技巧
-
默认使用非贪婪匹配:尤其是在 HTML、XML 或 JSON 文本处理中,可以防止匹配范围过大。
-
结合分组优化匹配结果:使用括号
()捕获非贪婪匹配的特定内容。text = "<a href='link1'>A</a><a href='link2'>B</a>" pattern = r"href='(.*?)'" print(re.findall(pattern, text)) # 输出 ['link1', 'link2'] -
调试时用 re.findall() 多次测试:通过结果长度判断匹配是否过度。
七、小结
贪婪与非贪婪匹配的区别是正则表达式灵活性的关键所在:
- 贪婪:尽可能多地匹配(默认行为)
- 非贪婪:尽可能少地匹配(在量词后加
?)
掌握这两种匹配方式,可以帮助你更精准地处理文本提取、数据清洗以及复杂字符串解析任务。