正则表达式完美匹配,但在替换时跳过匹配

RegEx matches perfectly but skips matches when replacing

提问人:droptix 提问时间:9/21/2023 更新时间:9/21/2023 访问量:45

问:

我有一个包含占位符的文本,例如.%%%NAME.suffix|DefaultValue%%%

以下正则表达式与它完美匹配(见链接):%{3}((\w+)(?:\.(\w+))?)(?:\|([\s\S]*?))?%{3}

但是当我使用 while 循环在 JavaScript(ES5,ES6 或更高版本中相同)中逐个替换它们时,它会跳过匹配项。

我没有使用 RexEx 模式,而是使用一个简单的字符串。String.replace(/a/gm, "b")String.replace("%%%NAME.suffix|DefaultValue%%%", "DefaultValue")

var soup = "%%%X.x|a%%% %%%Y.y|b%%% %%%Z.z|c%%%";

var m;
while ((m = regex.exec(soup)) !== null) {
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) {
        regex.lastIndex++;
    }
    console.log(m[2] + " found: " + JSON.stringify(m, null, 2));
    soup = soup.replace(m[0], m[4]);
}

结果:跳过占位符。为什么?Y

输出:

X found: [ "%%%X.x|a%%%", "X.x", "X", "x", "a" ]
Z found: [ "%%%Z.z|c%%%", "Z.z", "Z", "z", "c" ]

另请参阅此处的完整代码:https://runjs.co/s/IcNbKwSCu

JavaScript 正则表达式 替换

评论

0赞 Pointy 9/21/2023
代码中显示的实际正则表达式应发布在问题中。指向外部网站的链接可能会随着时间的推移而中断。
1赞 Wiktor Stribiżew 9/21/2023
由于您不是在替换内联,而是在替换搜索匹配项的实际文本,并且当您更改它时,其长度会发生变化,并且正则表达式索引开始指向错误的位置。你只需要直接使用。.replace
0赞 Pointy 9/21/2023
此外,您的正则表达式不可能具有零字符匹配。
0赞 Wiktor Stribiżew 9/21/2023
所以,在我看来,你所需要的只是如果soup = soup.replace(regex, '$4')const regex = /%{3}((\w+)(?:\.(\w+))?)(?:\|([\s\S]*?))?%{3}/g
0赞 droptix 9/21/2023
@pointy它包含在我的问题中

答:

1赞 Alexander Nenashev 9/21/2023 #1

当您在正则表达式时修改字符串时,您将开始在每个循环中正则表达式一个新字符串。这似乎相当混乱。

使用 的迭代器:.matchAll()

更新

由于 OP 声明仅支持 es5,因此只需对字符串的副本执行:

var regex = /%{3}((\w+)(?:\.(\w+))?)(?:\|([\s\S]*?))?%{3}/g;

var soup = "%%%X.x|a%%% %%%Y.y|b%%% %%%Z.z|c%%%";
var soupCopy = soup;
var m;
while(m = regex.exec(soupCopy)){
    console.log(m[2] + " found: " + JSON.stringify(m));
    soup = soup.replace(m[0], m[4]);
}

console.log(soup);

与捕获组引用相同:

const regex = /%{3}((\w+)(?:\.(\w+))?)(?:\|([\s\S]*?))?%{3}/g;

var soup = "%%%X.x|a%%% %%%Y.y|b%%% %%%Z.z|c%%%";

console.log(soup.replace(regex, '$4'));

评论

0赞 droptix 9/21/2023
好!似乎有效,但不幸的是仅在 ES6+ 中。我的应用程序使用基于 ES5 的 Rhino JS 引擎运行,因此不可用:-(String.matchAll
1赞 droptix 9/21/2023
尊!如此简单和有效:非常感谢!更新的代码示例:runjs.co/s/YEcX7341v