Javascript 正则表达式:如何避免捕获组变得"undefined"?



如果我想捕获例如圆括号或方括号中的文本,并使用以下正则表达式:

[(.+)]|((.+))

我得到的例子是";[测试]";结果";测试";以及";未定义的";并且对于";(测试(";结果";未定义的";以及";测试";。我怎么能设法只得到";测试";结果呢?

(这个正则表达式只是一个例子,我实际的正则表达式更复杂,但也有同样的问题。(

如果您先使用前瞻匹配任一选项,然后在第二次传递中再次捕获,则可以将匹配转化为单个捕获。

最简单的方法也使用其他捕获:

(?=[(.+?)]|((.+?)))[([](12)[)]]

工作方式:匹配[...](...)作为前瞻,将分隔符之间的文本捕获为捕获1或2。然后,它再次捕获相同的文本,忽略分隔符,通过反向引用12,依靠对非参与匹配的反向引用来匹配空字符串。通过这种方式,相同的字符串被捕获到捕获3中,捕获3总是参与其中。

这可能相当有效。对相同位置的背面引用应该很快匹配。

如果这还不够好,并且您想要一个带有精确一个捕获的RegExp,即[..](..)之间的文本,那么我会尝试查找behinds

[([](.+?)(?:(?=))(?<=(1)|(?=])(?<=[1))

它匹配一个[(,然后尝试在它之后找到一个捕获,然后是)],然后它进行向后检查,看看前导分隔符是否分别是匹配的([

不太可能如此高效,但只匹配(...)[...],并在单个捕获中捕获它们之间的内容。如果对同一位置的回溯引用是有效的(虽然不能保证,但可能(,那么它可能还不错。如果它没有效率,它可能会进行大量的回顾(但只有在看到可能的结束-)或-]时(。

它也可以转换为只匹配您想要的文本的RegExp,所以";捕获零";是通过将前面的[(与后面的查找进行匹配而得到的结果(以及它内部使用的捕获1(:

(?<=[([])(.+?)(?:(?=))(?<=(1)|(?=])(?<=[1))

(Look behinds,and-ahead,真的是RegExp力量不断送出的礼物。Look behead和Look background都允许你使用不同的RegExp多次匹配同一个子字符串,甚至允许后面的引用早期匹配的捕获。(

如果捕获的特定组号无关紧要,只在乎它们包含的文本,我认为最简单的事情就是在之后过滤匹配项以删除未定义的组:

for (const match of ' [foo] (bar) '.matchAll(/[(.+)]|((.+))/g)) {
const [, text] = match.filter(m => m !== undefined);
console.log(text);
}

最新更新