<p>贪婪匹配找“最后一个合法出口”,懒惰匹配找“第一个合法出口”;. 先吞尽字符再回溯,.? 遇首匹配即停,但需注意换行和边界符干扰。</p>

因为 .* 是贪婪量词,它会“一路吃到末尾”,只要后面还有能匹配的字符,就不会停——哪怕已经满足了当前模式的最小要求。
贪婪匹配的本质是“先吞后退”
正则引擎处理 .* 时,并不是边扫边判断是否该停,而是尽可能多地消耗字符,直到整条表达式无法继续匹配为止;再通过回溯(backtracking)一点点往回缩,寻找能让整个模式成立的最长子串。
- 例如在字符串
"a1b2b"中匹配a.*b,.*先吞下"1b2",发现末尾有b,于是完整匹配成"a1b2b" - 即使中间就有一个更短的
"a1b",引擎也不会主动选它——除非你告诉它“别贪”,也就是改用懒惰模式
常见过载场景:引号、标签、括号成对结构
当边界符重复出现时,.* 容易跨过第一个闭合符,直奔最后一个去:
-
"(.*)"匹配"他说:"你好,世界!" 然后离开。"→ 拿到"你好,世界!" 然后离开。 -
<div>(.*)</div>匹配<div>A</div>xxx<div>B</div>→ 可能捕获整个A</div>xxx<div>B - 原因相同:它不认“第一个结束”,只认“最后一个还能让表达式成立的位置”
懒惰匹配不是万能解药,但它是精准控制的起点
把 .* 改成 .*? 能让匹配在遇到第一个满足条件的结尾时立刻停止,但它仍依赖后续字符确实存在且可识别。
- 推荐组合:
"([^"]*)"比"(.*?)"更安全——直接排除引号本身,避免意外中断或换行干扰 - 注意点:
.*?在含换行的文本中默认不匹配\n,需加re.DOTALL标志才生效 - 真正健壮的提取,往往要结合具体字符集(如
[^>]*替代.*?匹配 HTML 标签内容)
一句话记住关键区别
贪婪匹配找“最后一个合法出口”,懒惰匹配找“第一个合法出口”——而正则引擎默认只认最后那个。

















