讲解

环视(lookaround)是「只检查不匹配」的断言:它断言某个位置的前面或后面是什么样的文本,但断言的文本不进入匹配结果。四种形式:(?=...) 正向前瞻——后面必须能匹配 ...;(?!...) 负向前瞻——后面必须不能匹配;(?<=...) 正向后顾——前面必须能匹配;(?<!...) 负向后顾。它们和锚点一样是零宽的,不消耗字符。

前瞻最经典的应用是多条件密码校验:要求「至少 8 位、同时含字母和数字」本质是两个独立的「存在性」条件,用普通写法很难表达,用前瞻就顺理成章:^(?=.[a-zA-Z])(?=.\d) 在开头位置连续断言「后面某处存在字母」「后面某处存在数字」,然后主体再校验长度和字符集。因为前瞻不消耗字符,多个前瞻可以在同一位置叠加。

后顾的典型场景是「取某标记之后的值」:(?<=¥)\d+ 匹配前面是 ¥ 的数字,匹配结果里不含 ¥ 本身。两个注意点:一是兼容性,JavaScript 的后顾是 ES2018 特性,Safari 16.4 起才支持,老旧环境慎用;二是负向环视挡不住「部分匹配」——(?<!¥)\d+ 面对 ¥199 时,引擎会退一步从第二个数字开始匹配 99,绕开断言。要表达「完整的、前面不是 ¥ 的数字」,断言里还得把数字也排除:(?<![¥\d])\d+。

示例

// 正向前瞻 (?=...):匹配后面跟着「元」的数字
const s = '价格 100 元,折扣 20 元,数量 3 件';
console.assert(s.match(/\d+(?= 元)/g).join(',') === '100,20');

// 环视是零宽的:匹配结果里不包含断言的文本
const m = 'foo123'.match(/foo(?=\d+)/);
console.assert(m[0] === 'foo'); // 只有 foo,数字只是「条件」

// 负向前瞻 (?!...):后面不能是指定内容
console.assert(/q(?!u)/.test('Iraq')); // q 后面不是 u
console.assert(/q(?!u)/.test('quit') === false); // q 后面是 u,失败

多前瞻叠加做密码校验:

// 至少 8 位、必须同时含字母和数字(只允许这两类字符)
const pwd = /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
console.assert(pwd.test('abc12345'));
console.assert(pwd.test('Passw0rd'));
console.assert(pwd.test('abcdefgh') === false); // 缺数字
console.assert(pwd.test('12345678') === false); // 缺字母
console.assert(pwd.test('ab12') === false); // 太短

后顾与「部分匹配」陷阱:

// 正向后顾 (?<=...):取 ¥ 后面的金额,结果不含 ¥
const price = '原价¥199,现价¥99';
console.assert(price.match(/(?<=¥)\d+/g).join(',') === '199,99');

// 陷阱:负向后顾挡不住部分匹配
const p2 = '价格¥199';
console.assert(p2.match(/(?<!¥)\d+/g)[0] === '99'); // 引擎从第二个 9 开始照样匹配!

// 正确写法:断言「前面既不是 ¥ 也不是数字」,锁定完整数字的起点
console.assert(p2.match(/(?<![¥\d])\d+/g) === null);
console.assert('运费 20'.match(/(?<![¥\d])\d+/g)[0] === '20'); // 正常数字不受影响

常见坑

  • 以为环视会消耗字符:环视只断言位置,(?=\d)\d 才匹配一个数字,单写 (?=\d) 匹配的是空位置。想要字符进结果,环视后面还得有真正的匹配元素。
  • 忽略后顾的兼容性:JavaScript 后顾在 Safari 16.4 之前不支持,正则字面量会直接抛语法错误让整个脚本失效。面向老旧浏览器时改用捕获组替代。
  • 低估负向环视的部分匹配问题:(?<!X) 只约束匹配起点前一个字符,引擎移动起点就能绕过。约束「完整片段」时把前缀字符类一起排进断言。
  • 环视层层嵌套:环视里套环视理论上可行,可读性极差。超过两层就该拆成多步匹配或用代码判断。

小结

(?=) (?!) (?<=) (?<!) 是零宽断言,只检查不匹配;多前瞻叠加擅长多条件校验,后顾注意兼容性和部分匹配陷阱。下一节讲引用前面捕获内容的反向引用。