跳到主要内容

正则表达式前瞻与后顾

长念
长念阅读约 3 分钟3 年前发布2 年前编辑
特别注意

Safari 版本低于 16.3 不支持零宽断言,谨慎使用!或使用替代方案

前瞻

exp(?=pattern) 匹配后面是 patternexp

全称零宽正向先行断言。从匹配的位置 向右 看,匹配一些字符,但是并不包含匹配模式,只是确定是否匹配,也就是所谓的零宽。将其中的等号 = 替换为感叹号 ! 即为 零宽负正向先行断言

后顾

(?<=pattern)exp 匹配前面是 patternexp

全称零宽正向后行断言。从匹配的位置 向左 看,是否满足期望匹配规则。将其中的等号 = 替换为感叹号 ! 即为 零宽负正向后行断言

示例

const str = 'netlify internet';

str.match(/net(?=lify)/);
// ['net', index: 0, input: 'netlify internet', groups: undefined];

str.match(/net(?!lify)/);
// ['net', index: 13, input: 'netlify internet', groups: undefined];

str.match(/(?<=inter)net/);
// ['net', index: 13, input: 'netlify internet', groups: undefined]

str.match(/(?<!inter)net/);
// ['net', index: 0, input: 'netlify internet', groups: undefined]

替代方案

可以使用分组匹配,然后只获取我们期望的匹配组。

匹配 lify 前面的所有非空白字符

const str = 'netlify';
const res = str.match(/(\S*)lify/);
// Output: ['netlify', 'net', index: 0, input: 'netlify', groups: undefined]

const matched = res[1];
// Output: net

匹配所有 <h2></h2> 中间的所有非空白字符,即解析 html 中的二级标题。global 匹配,注意 matchAll 返回的是迭代器 Iterator

const html = `
<h2>First</h2>
<h2>Second</h2>
`;

const res = html.matchAll(/<h2>(\S*)<\/h2>/g);
// Output: RegExpStringIterator {}

console.log([...res]);
// Output: [
// ['<h2>First</h2>', 'First', index: 3, input: '\n <h2>First</h2>\n <h2>Second</h2>\n ', groups: undefined]
// ['<h2>Second</h2>', 'Second', index: 20, input: '\n <h2>First</h2>\n <h2>Second</h2>\n ', groups: undefined]
// ];

// 注意: Iterator 只能被迭代一次,此处为示例
const titles = [...res].map((r) => r[1]);
// Output: ['First', 'Second']
注意

迭代器只能被迭代一次。如上示例,执行 console.log([...res]) 之后,后面再迭代 [..res] 已经没有可迭代的数据了。