正则前瞻匹配(零宽度先行断言)

基于 ECMAScript 正则表达式语法

断言匹配条件不消耗输入中的任何字符,可参考 ^ $,只断言是文本开头或者结尾,不消耗任何字符。

零宽度正向先行断言

(?=...) 当 … 能在当前位置匹配输入,则匹配。

例如对 baaba 匹配 (?=(a+))a*b,匹配过程如下。

  • 从位置 0 开始,字符 b 不符合 a+,前瞻匹配失败,移动到下一位置。
  • 从位置 1 开始,a+ 贪婪匹配 aa(位置 1-2),前瞻匹配成功,不消耗字符,当前位置仍在位置 1。
  • 从位置 1 继续匹配 a*ba* 贪婪匹配 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 的条件。

零宽度负向先行断言

(?!...) 当 … 不能在当前位置匹配输入,则匹配。

零宽度负向先行断言和正向的相反,类比零宽度正向先行断言即可。