正则先行断言与后行断言:零宽匹配的妙用
(?=) (?!) (?<=) (?
一、什么是零宽断言
断言(assertion)匹配的是位置条件而不是字符:判断成立与否,但不消费任何字符。
- 正向先行
(?=X):后面要紧跟 X。 - 负向先行
(?!X):后面不能是 X。 - 正向后行
(code(?<=X))即(?<=X):前面要是 X。 - 负向后行
(?<!X):前面不能是 X。
二、经典案例:金额加千分位
给长数字每三位加逗号:1234567 → 1,234,567
正则:(?<=\d)(?=(\d{3})+$) 替换为逗号。
解读:位置前是数字(后行断言),且后面恰好是 3 的倍数个数字直到结尾(先行断言)——两个零宽断言夹出一个插入点,替换就是在这些位置插逗号。
三、常用技巧
- 密码强度校验:
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#]).{8,}$——四个先行断言分别要求「某处有小写/大写/数字/符号」,顺序不限。 - 排除特定词:
^(?!admin$).+匹配除 admin 外的用户名。 - 提取上下文:
(?<=price.: )\d+只取 price 后面的数字。
四、注意事项
- 后行断言定长限制:部分引擎(含老版 JS、Python re)要求 (?<=) 内长度固定,不能 (?<=\d+)。
- JS 兼容性:ES2018 才支持后行断言,老旧浏览器(含部分国产浏览器内核)会直接语法报错。
- 性能:嵌套断言可能引发大量回溯,长文本上先测性能再上生产。
在线体验
不用装任何环境,直接用本站正则在线测试工具在线操作,数据都在浏览器本地处理,方便又安全。
常见问题
断言里的内容会出现在匹配结果里吗?
不会。零宽断言只做条件判断不消费字符,捕获组内容也只在断言组内部有效((?: ) 非捕获版更干净)。
?= 和 ?: 有什么区别?
(?=X) 是先行断言:要求后面是 X 但不消费;(?:X) 是非捕获分组:正常匹配 X 只是不保存捕获。前者零宽,后者消费。
所有语言都支持四种断言吗?
主流语言都支持先行断言;后行断言在 ES2018+、PCRE、Python 3.7+(含变长支持)、Java、.NET 可用,老 JS 引擎不支持,跨端运行的正则要确认目标环境。
