正则贪婪匹配与非贪婪匹配:一次讲透 .* 和 .*? 的区别
.* 会一口气吃到行尾再回溯,.*? 只吃到最近满足条件的位置。用实例讲清贪婪、懒惰与回溯机制,避免经典匹配错误。
一、默认都是贪婪的
正则里的量词 * + {n,} 默认贪婪:尽可能多地匹配。对字符串 <a>hello</a><a>world</a> 用 <.+> 匹配,结果是整个字符串——因为 .+ 一路吃到行尾,再回溯找到最后一个 >。
二、加个问号变懒惰
量词后加 ? 就是非贪婪(懒惰):尽可能少匹配。<.+?> 匹配上面同样的字符串,得到两个结果:<a> 和 </a>(各自单独),因为它遇到第一个满足条件的 > 就停下。
抓取 HTML 标签内容 <a>(.+?)</a> 时,懒惰模式才能正确分组捕获。
三、回溯:性能杀手
贪婪匹配「先吃过头再吐回来」的过程叫回溯。灾难性例子:对长字符串用 (a+)+b 且字符串末尾没有 b,回溯次数呈指数增长,正则引擎直接卡死——这也就是 ReDoS(正则拒绝服务)攻击的原理。
- 能用具体字符类(
[^<])就别用万能点号.,减少回溯空间。 - 养成「精确匹配优先」的习惯,性能和正确率都更好。
四、三个实用对照
"(.*)"贪婪:一行有两个引号对时只匹配一大段;"(.*?)"懒惰:正确匹配每一对引号。\d{2,}匹配 12345 会吃满 5 位;\d{2}?只匹配 2 位。- 独占模式
.*+(部分语言支持)不回溯,要么全匹配要么失败,可用于锁死性能。
在线体验
不用装任何环境,直接用本站正则在线测试工具在线操作,数据都在浏览器本地处理,方便又安全。
常见问题
.* 和 .*? 到底用哪个?
取决于需求:想匹配到一行中「最后」某个边界用 .*,想匹配「最近」的边界用 .*?。提取成对标签、引号内容时几乎都用懒惰模式。
为什么我的正则把整行都匹配了?
大概率中了贪婪陷阱。检查量词(* + {n,}),在后面加 ? 改成非贪婪试试,或把 . 换成排除字符类 [^x]。
正则能解析 HTML 吗?
简单固定的片段可以,完整 HTML 解析不行(嵌套结构超出了正则能力,属上下文无关文法)。正式场景请用 DOM 解析器,正则只做辅助。
