正则表达式的exec方法在第二次搜索时返回null。

7

使用场景

我想要搜索一个字符串以获取多个匹配项。每个匹配项最终都与对象数组中的一个属性链接。当找到一个匹配时,该匹配将被对象内另一个属性替换。问题在于,代码始终会在第二个匹配项上返回 null。

测试用例

这是我正在使用的测试用例。为了简化问题,我只将所有匹配项替换为数字5,但请注意,最终代码将用变量值替换匹配项。

测试代码

下面是我用来测试和调试问题的代码。有趣的是,如果我更改 var str = '5 + QUESTION_2'QUESTION_2 就会成功替换为 5。本质上,问题归结为第二个匹配项始终返回 null,尽管它能够匹配。

var re = /( |^)(QUESTION_1|QUESTION_2|QUESTION_3)( |$)/g;
var str = 'QUESTION_1 + QUESTION_2';
var rep = 5;

matches = re.exec(str);
var re2 = new RegExp("( |^)(" + matches[2] + ")( |$)", "g");
console.log(matches); // Returns a match on QUESTION_1
str = str.replace(re2, rep);
console.log(str); // Returns 5+ QUESTION_2

matches = re.exec(str);
console.log(matches); // Returns a match on NULL - doesn't find QUESTION_2
re2 = new RegExp("( |^)(" + matches[2] + ")( |$)", "g");
str = str.replace(re2, rep);
console.log(str); // Returns 5+ QUESTION_2

问题

  • 为什么第二个匹配总是 null?
  • jsfiddle 在此处可用。

所选答案正确地识别了问题,但我认为你应该按照我的建议去做,而不是依赖于多个execs和调整lastIndex - plalx
2个回答

12

因为你在调用exec时使用了全局标志,正则表达式引擎会记住lastIndex。lastIndex是一个读/写整数属性,用于指定正则表达式下一次匹配开始的索引位置。

将以下代码添加以重置它:

re.lastIndex=0;

在下一次调用 RegExp.exec 之前,re.lastIndex = 0 可以让你重置搜索索引并从开头开始第二次搜索。如果没有重置它,搜索将从最后一个匹配的位置开始,这种情况下会返回 null

阅读这篇 Mozilla 文档,获取更多信息。根据该手册:

只有在正则表达式使用 "g" 标志进行全局搜索时,此属性才会被设置。


2

在全局正则表达式上调用 exec() 时,它会记住上次匹配的位置,并从那里开始下一次匹配。

var rx = /a/g, matches = rx.exec('aa');
rx.lastIndex; //1

当你在exec调用之间修改输入字符串时,lastIndex会变得不同步。
虽然有一些方法可以解决这个问题,但我宁愿在单个语句中执行替换。
'QUESTION1 + QUESTION2 + QUESTION3'.replace(/QUESTION\d+/g, function ($1) { 
    return 'VALUE_FOR_' + $1; 
});

//"VALUE_FOR_QUESTION1 + VALUE_FOR_QUESTION2 + VALUE_FOR_QUESTION3"

有趣的解决方案。我需要尝试一下这个。实际代码中的用例有点更加复杂,但我认为这可以被利用并且可以简化我的生产问题。在生产问题中,替换也是可变的,我只是使用了“QUESTION_?”来简化它。然而,对于这个特定的测试用例,这是一个更加优雅的解决方案+1。 - Pete
@mister_rampage 你的意思是根据最后一次匹配,需要替换的内容可能会有所不同?如果你能解释清楚你的实际问题,也许我可以帮你找到一个可行的解决方案。 - plalx
只是想让你知道,你的解决方案非常棒。比原始的循环解决方案更加优雅。非常感谢你的帮助。我希望我能接受两个答案。 - Pete
@mister_rampage 很高兴听到这个消息。不幸的是,这是不可能的,你必须选择更好地回答问题的那一个。不仅是为了你,也是为了其他人 ;) 我只想说将 lastIndex 设置回 0 可能会起作用,但并非在每种情况下都是如此,因为它将从字符串的开头重新开始匹配。根据你的替换,它可能会产生奇怪的行为。 - plalx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接