String.prototype.matchAll()是否可返回重叠正则匹配结果?
能否让String.prototype.matchAll()返回重叠匹配结果?
问题背景
我尝试给String.prototype.matchAll()传入正则表达式,希望它返回重叠的匹配结果,但所有思路都只能得到非重叠的匹配项。测试代码如下:
function logMatches(str, re) { console.log(`"${str}".matchAll(/${re.source}/${re.flags}) = [`); [...str.matchAll(re)].map(matchProperties).forEach((props) => { console.log(" ", props); }) console.log("];"); console.log(); } function matchProperties(match) { return "{ " + (Object.getOwnPropertyNames(match).map((propName) => { return propName + ": " + JSON.stringify(match[propName]) }).join(", ")) + " }, // Note: this element is an Array"; } logMatches("foo bar baz", /foo|baz/g); // no overlap expected logMatches("foo bar baz", / .*/g); logMatches("foo bar baz", /b.*/g); logMatches("foo bar baz", /b.*?/g); // no overlap expected logMatches("foo bar baz", /ba.*$/g); logMatches("foo bar baz", /ba| .* /g);
我期望最后一个示例返回包含重叠匹配的结果:
"foo bar baz".matchAll(/ba| .* /g) = [ { 0: " bar ", length: 1, index: 3, input: "foo bar baz", groups: undefined }, // Note: this element is an Array { 0: "ba", length: 1, index: 4, input: "foo bar baz", groups: undefined }, // Note: this element is an Array { 0: "ba", length: 1, index: 8, input: "foo bar baz", groups: undefined }, // Note: this element is an Array ];
原因分析
这是正则表达式的通用特性,既不是matchAll的工作机制问题,也不是你的示例代码有bug。
正则引擎默认采用非重叠匹配逻辑:每次匹配成功后,会从当前匹配结果的结束位置开始下一次匹配,不会回溯到之前的位置寻找重叠的结果。比如你最后一个例子里的/ba| .* /g:
- 第一次匹配到
" bar "(索引3到7),下一次匹配直接从索引7开始; - 索引7之后只有
"baz",所以只能匹配到索引8的"ba",不会回到索引4去匹配重叠的"ba"。
实现重叠匹配的方法
如果要得到重叠的匹配结果,需要借助零宽度断言(比如正向预查(?=...))。零宽度断言不会"消耗"字符串中的字符,匹配成功后引擎的位置不会移动,这样就能在同一位置继续寻找其他匹配项。
针对你的需求,可以修改正则为:
// 使用正向预查捕获重叠内容,同时过滤空匹配 const re = /(?=( .* ))|ba/g; const matches = [...("foo bar baz".matchAll(re))].filter(m => m[0] !== ""); // 处理并输出匹配结果 console.log(`"foo bar baz".matchAll(/${re.source}/${re.flags}) = [`); matches.map(matchProperties).forEach(props => console.log(" ", props)); console.log("];");
这个正则的逻辑是:
(?=( .* )):正向预查匹配" bar ",但不消耗字符,匹配后位置仍停在索引3;ba:接着可以匹配到索引4的"ba",之后位置移动到6,继续往后匹配到索引8的"ba";- 最后过滤掉空匹配(因为正向预查本身不会生成匹配内容,需要从捕获组提取或调整正则避免空匹配)。
总结
- 默认正则匹配是非重叠的,这是所有正则引擎的通用行为;
matchAll只是按照正则引擎的结果返回匹配项,本身不会改变匹配逻辑;- 要实现重叠匹配,必须通过零宽度断言等技巧让引擎不消耗字符,从而在同一位置继续查找。
内容的提问来源于stack exchange,提问作者Daniel Kaplan
相关产品推荐
相关产品推荐

