正则前瞻匹配(零宽度先行断言)
基于 ECMAScript 正则表达式语法
断言匹配条件不消耗输入中的任何字符,可参考 ^ $,只断言是文本开头或者结尾,不消耗任何字符。
零宽度正向先行断言
(?=...)当 … 能在当前位置匹配输入,则匹配。
例如对 baaba 匹配 (?=(a+))a*b,匹配过程如下。
- 从位置 0 开始,字符
b不符合a+,前瞻匹配失败,移动到下一位置。 - 从位置 1 开始,
a+贪婪匹配aa(位置 1-2),前瞻匹配成功,不消耗字符,当前位置仍在位置 1。 - 从位置 1 继续匹配
a*b,a*贪婪匹配aa(位置 1-2),然后b匹配位置 3 的b成功。 - 最终匹配结果为
aaba(位置 1-3),分组 1 捕获aa。
再来看一个例子,对 abc2ABC 匹配 (?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,}。这个正则是匹配包含大写字母、小写字母和数字,且长度至少为 6 的字符串。
首先看第一个前瞻匹配 (?=.*[a-z]),用来判断是否包含小写英文字母。
- 从位置 0 开始,
.*贪婪匹配abc2ABC这七个字符,导致[a-z]匹配失败。 .*回溯后匹配abc2AB这六个字符,剩下C,[a-z]匹配失败。.*回溯后匹配abc2A这五个字符,剩下B,[a-z]匹配失败。.*回溯后匹配abc2这四个字符,剩下A,[a-z]匹配失败。.*回溯后匹配abc这三个字符,剩下2,[a-z]匹配失败。.*回溯后匹配ab这两个字符,剩下c,[a-z]匹配c成功。- 第一个前瞻匹配成功,当前位置仍在位置 0。
第二个前瞻匹配 (?=.*[A-Z]),用来判断是否包含大写英文字母。
- 从位置 0 开始,
.*贪婪匹配abc2ABC这七个字符,导致[A-Z]匹配失败。 .*回溯后匹配abc2AB这六个字符,剩下C,[A-Z]匹配C成功。- 第二个前瞻匹配成功,当前位置仍在位置 0。
第三个前瞻匹配 (?=.*[0-9]),用来判断是否包含数字。
- 从位置 0 开始,
.*贪婪匹配abc2ABC这七个字符,导致[0-9]匹配失败。 .*回溯后匹配abc2AB这六个字符,剩下C,[0-9]匹配失败。.*回溯后匹配abc2A这五个字符,剩下B,[0-9]匹配失败。.*回溯后匹配abc2这四个字符,剩下A,[0-9]匹配失败。.*回溯后匹配abc这三个字符,剩下2,[0-9]匹配2成功。- 第三个前瞻匹配成功,当前位置仍在位置 0。
最后 .{6,} 从位置 0 匹配 abc2ABC 这七个字符,匹配完成。
最终匹配结果为 abc2ABC,满足包含大写字母、小写字母和数字,且长度至少为 6 的条件。
零宽度负向先行断言
(?!...)当 … 不能在当前位置匹配输入,则匹配。
零宽度负向先行断言和正向的相反,类比零宽度正向先行断言即可。