替换全局正则表达式中仅匹配的第一个结果

7
我正在编写一个递归替换字符串中正则表达式匹配项的函数。替换内容可以是一个函数,就像普通的.replace一样,而且这个函数可以通过其中一个参数访问原始字符串。
我希望我的函数每次迭代只替换一个匹配项。对于非全局正则表达式,这总是成立的。然而,这个函数接收到的一些正则表达式将是全局的。使用传统的.replace(正则表达式,替换内容)意味着它可能在每次迭代中多次替换,不仅会打乱处理匹配项的顺序,而且还会向替换函数传递错误的索引和原始字符串。
例如:
function recursiveReplace(string, regex, replacement) {
  for (var i = 1e8; i > 0 && regex.test(string); i--)
    string = string.replace(regex, replacement);
  return string;
}

console.log(
  recursiveReplace("abcdef", /../g, function (match, index, original) {
    console.log(original);
    return match[0];
  })
);

这将输出

abcdef
abcdef
abcdef
ace
ae
a

当期望的输出为:
abcdef
acdef
adef
aef
af
a

我应该如何让函数在每次迭代中只处理一个匹配项,无论正则表达式是否带有 g 标志?请注意,我使用的函数总是将第二个参数作为正则表达式(我无法控制这一点,也无法控制所述正则表达式是否带有 g 标志)。

1e8 总是等于 1 吗? - user557597
@WiktorStribiżew 很有趣,我以前从未听说过这个。你的建议问题在于我正在使用该函数的地方,第二个输入将始终是正则表达式(我无法控制)。 - ETHproductions
由于“replacement”是一个字符串替换模式,您最好从正则表达式对象中解析出模式和标志,重新创建而不使用“g”。 - Wiktor Stribiżew
@WiktorStribiżew 看来我只需要使用 regex = RegExp(regex, regex.flags.replace('g', '')); 就可以解决问题了。我想问题解决了,但我不确定这在跨平台方面是否有效。 - ETHproductions
1
@sln xeyxEy 表示 x * 10**y(其中 ** 表示指数运算) - Nick stands with Ukraine
显示剩余2条评论
1个回答

2

看起来最好的方法是手动从正则表达式中删除g标志。这是我找到的最跨平台的方法,使用regex.toString()获取正则表达式的字符串表示:

function recursiveReplace(string, regex, replacement) {
  regex = eval(regex.toString().replace(/[a-z]*$/, function (s) {
    return s.replace('g', '');
  }));
  for (var i = 1e8; i > 0 && regex.test(string); i--)
    string = string.replace(regex, replacement);
  return string;
}

有了ES6功能的支持,使用RegExp(regex)RegExp#flags 就变得更加容易了:

function recursiveReplace(string, regex, replacement) {
  regex = RegExp(regex, regex.flags.replace('g', ''));
  for (var i = 1e8; i > 0 && regex.test(string); i--)
    string = string.replace(regex, replacement);
  return string;
}

.flags 不是 ES6(虽然许多支持它),但您可以使用标准化的 regex.toString() 并手动提取标志。 - Downgoat
是的,true,而且在IE、Opera和Safari中也不支持flags属性。支持.toString() - Wiktor Stribiżew
我已经添加了一种不使用ES6功能的方法。感谢大家的帮助。 - ETHproductions
请原谅我如果我误解了,但是这样不会匹配所有的 g 出现次数吗?这样会从原始正则表达式中剥离它们。例如,如果我们使用 [a-g] 而不是 [a-z],那么这不是一个问题吗?最后一个 / 后面可能有五个表达式标志,它们是 gimuy,所以我认为我们需要一个正则表达式仅匹配 return s.replace(); 中的最后一个 g。如果您知道这种语法是什么样子,请分享一下。 - Mentalist

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