### 一、先破后立:预查不是“匹配”,是“瞄准” 很多人初学正则时,会把`?=`这类符号当成普通匹配符,其实它们本质是**“位置锚定工具”**——不占用匹配结果的字符,只负责判断“当前位置前后是否符合条件”。
打个比方:你想在超市找 “买一送一” 的牛奶,“牛奶” 是要匹配的目标,“买一送一” 就是预查条件 —— 它帮你定位到符合要求的牛奶,但最终你拿的还是牛奶本身,不是 “买一送一” 这几个字。
关键记忆点:
- 有
<
就是 “看前面”(后行),没有就是 “看后面”(先行); =
是 “必须有”(正向),!
是 “不能有”(负向)。
很多人会写[A-Za-z0-9]{8,16}
,但这无法强制 “同时有大写和数字”。用正向预查就能解决:
^(?=.*[A-Z])(?=.*\d)[A-Za-z0-9]{8,16}$
(?=.*[A-Z])
:从开头位置判断,后面任意字符后必须有大写字母;(?=.*\d)
:同时判断后面任意字符后必须有数字;- 最后才匹配 8-16 位字母数字 —— 三者缺一不可。
手机号规则是 “1 开头的 11 位数字”,但要排除 170 开头的:
(?!170)
:开头位置后面不能跟着 “170”;- 再匹配 11 位数字,完美排除无效号码。
直接匹配数字会拿到所有数字,用正向后行预查定位 “价格” 后的数字:
(?<=价格)
:只匹配 “前面是‘价格’” 的位置;- 后面的
\d+
就是我们要的金额,不会包含 “价格” 二字。
如果直接替换 “苹果” 会误伤,用负向后行预查精准定位:
(?<!红色的)
:前面不是 “红色的” 的 “苹果” 才会被匹配;- 替换后 “红色的苹果” 不变,“绿色的苹果” 变成 “绿色的水果”。
日志格式:[2024-05-01] ERROR: 连接失败
/ [2024-05-01] ERROR: Timeout
ERROR:
是固定前缀;(?!Timeout)
:排除后面跟着 “Timeout” 的情况;.*
提取真正需要关注的错误描述。
预查内容不占匹配长度:比如用a(?=b)c
匹配 “abc” 会失败 —— 因为(?=b)
只定位 a 和 b 之间的位置,后面的 c 会去匹配 b 的位置,自然不匹配。正确写法是a(?=b)b
或a(?=b).
。
后行预查的长度限制:部分老版本正则引擎(如 JavaScript ES2018 前)不支持后行预查的变长匹配,比如(?<=a+)
会报错,需用固定长度如(?<=aa)
。
多个预查的顺序不影响结果:比如密码验证中(?=.*[A-Z])(?=.*\d)
和(?=.*\d)(?=.*[A-Z])
效果完全一致,因为都是对整个字符串的条件判断。
当你需要 “在匹配目标的同时,对目标的前后内容加限制,但又不要这些限制内容出现在结果里” 时,直接用预查就对了 —— 比嵌套匹配更简洁,比多次筛选更高效。
这些例子覆盖了日常开发和数据处理的常见场景,你可以直接拿去测试或修改。如果还有具体的正则需求(比如特定格式的文本处理、某个语言的兼容问题),欢迎告诉我,我可以帮你拆解更精准的表达式。