正则零宽断言(Lookahead / Lookbehind)

零宽断言匹配"位置条件"而非字符。包括正向/负向先行断言和正向/负向后行断言,用于精确限定匹配上下文。

#type / concept #status / evergreen #resource / javascript #resource / ecmascript

[!info] related notes

正则零宽断言(Lookahead / Lookbehind)

一句话定义

断言匹配的是”位置条件”,不消耗字符。它只检查”当前位置的前/后是否满足某条件”,但不会把检查的部分纳入匹配结果。

四种断言

断言名称含义
(?=...)正向先行当前位置后面必须跟 ...
(?!...)负向先行当前位置后面不能跟 ...
(?<=...)正向后行当前位置前面必须跟 ...
(?<!...)负向后行当前位置前面不能跟 ...

“先行”看右边,“后行”看左边。

实际例子

正向先行 (?=...)

匹配后面紧跟 px 的数字(不含 px):

"12px 30em 8px".match(/\d+(?=px)/g);
// ["12", "8"]

密码强度校验(同时满足多个条件):

// 必须包含大写、小写、数字,且至少 8 位
const reg = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$/;

reg.test("Abc12345");  // true
reg.test("abc12345");  // false(没大写)
reg.test("ABCDEFG1");  // false(没小写)

负向先行 (?!...)

匹配后面不是 px 的数字:

"12px 30em 8px".match(/\d+(?!px)/g);
// 注意:这会匹配到每个数字中"后面不是 px"的那个位置
// 实际使用中通常需要更精确的写法

更实用的场景:排除特定后缀

// 匹配不以 .min.js 结尾的文件名
"app.js app.min.js utils.js".match(/\w+(?!\.min)\.js/g);
// ["app.js", "utils.js"]

正向后行 (?<=...)

匹配美元符号后面的数字(不含 $):

"$199 ¥200 €300".match(/(?<=\$)\d+/g);
// ["199"]

匹配 color: 后面的值:

const str = "color: red; background: blue;";
str.match(/(?<=color:\s*)\w+/g);
// ["red"]

负向后行 (?<!...)

匹配前面不是 $ 的数字:

"$199 200 €300".match(/(?<!\$)\d+/g);
// 注意:会匹配数字中每个"前面不是 $" 的位置

更实用:匹配不在引号内的内容

// 匹配不在 HTML 标签内的文本(简化示例)
const str = "<b>bold</b> normal <i>italic</i>";
// 匹配不在 < > 之间的单词
str.match(/(?<=>)[^<]+/g);
// ["bold", " normal ", "italic"]

组合使用

// 匹配 "hello" 但仅当它在 "say" 后面且不在 "goodbye" 前面
const reg = /(?<=\bsay\s)hello(?!\s+goodbye)/;

reg.test("say hello");           // true
reg.test("say hello goodbye");   // false

常见场景

提取特定格式的值

// 从 CSS 中提取所有颜色值
const css = "color: #fff; background: #333; border: 1px solid #aaa";
css.match(/(?<=#)[0-9a-f]{3,6}/gi);
// ["fff", "333", "aaa"]

替换特定上下文中的内容

// 只替换冒号后面的数字(日期部分)
"2026:03:21".replace(/(?<=:)\d{2}/g, (m) => `[${m}]`);
// "2026:[03]:[21]"

条件过滤

// 匹配以 "http" 开头但不是 "https" 的 URL
const urls = "http://a.com https://b.com http://c.com";
urls.match(/http(?!s):\/\/[^\s]+/g);
// ["http://a.com", "http://c.com"]

注意事项

后行断言在老环境中不支持

正向先行和负向先行支持很广泛(ES5+)。

正向后行 (?<=...) 和负向后行 (?<!...) 是 ES2018 才正式标准化的,老浏览器和 Node 8 以下可能不支持。

断言不消耗字符

"12px".match(/\d+(?=px)/);
// 匹配结果是 "12",不是 "12px"
// 断言只检查位置,不纳入匹配内容

不能替代非贪婪

断言和非贪婪解决的问题不同:

  • 非贪婪:控制量词”吃多少”
  • 断言:限定匹配必须满足的”前后位置条件”

两者经常配合使用:

// 匹配 <div> 标签内的内容(不含标签本身)
const str = "<div>hello</div>";
const content = str.match(/(?<=<div>).*?(?=<\/div>)/);
// ["hello"]

速查

(?=...)   // 后面跟 ...
(?!...)   // 后面不跟 ...
(?<=...)  // 前面跟 ...
(?<!...)  // 前面不跟 ...
创建于 2026/3/21 更新于 2026/5/27