在Javascript中用大写替换正则表达式捕获组

IT技术 javascript regex replace uppercase
2021-01-27 02:57:46

我想知道如何在 JavaScript 中用大写替换捕获组。这是我迄今为止尝试过但不起作用的简化版本:

> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'

你能解释一下这段代码有什么问题吗?

6个回答

您可以将函数传递给replace.

var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });

解释

a.replace( /(f)/, "$1".toUpperCase())

在本例中,您将一个字符串传递给替换函数。由于您使用的是特殊的替换语法($N 获取第 N 个捕获),因此您只是给出了相同的值。这toUpperCase实际上是在欺骗,因为您只是将替换字符串设为大写(这有点毫无意义,因为$和 一个1字符没有大写,因此返回值仍然是"$1"

a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))

信不信由你,这个表达式的语义是完全一样的。

@Evan Carroll:请看我的回答。
2021-03-17 02:57:46
啊,我明白你的意思了,我在大写“\$1”。不是替换会做的巫毒教的结果显然是替换$1第一个捕获组。
2021-03-22 02:57:46
总体无关紧要,但不需要捕获组f (f),因为v它引用了组 0。请注意阅读此内容的人。在快速基准测试中,每个捕获组都将正则表达式的速度降低了 5%。
2021-03-22 02:57:46
@EvanCarroll 详细解释了为什么您的初始代码不起作用以及如何使其起作用,请参阅下面的答案。
2021-04-12 02:57:46

我知道我迟到了,但这里有一个更短的方法,更符合你最初的尝试。

a.replace('f', String.call.bind(a.toUpperCase));

那么你哪里出错了,这个新的伏都教是什么?

问题一

如前所述,您试图将被调用方法的结果作为String.prototype.replace()的第二个参数传递,而您应该传递对函数的引用

方案一

这很容易解决。简单地删除参数和括号会给我们一个引用而不是执行函数。

a.replace('f', String.prototype.toUpperCase.apply)

问题二

如果您现在尝试运行代码,您将收到一条错误消息,指出 undefined 不是函数,因此无法调用。这是因为 String.prototype.toUpperCase.apply 实际上是通过 JavaScript 的原型继承Function.prototype.apply()的引用所以我们实际上在做的事情看起来更像这样

a.replace('f', Function.prototype.apply)

这显然不是我们的意图。它怎么知道String.prototype.toUpperCase()运行Function.prototype.apply ()

解决方案2

使用Function.prototype.bind()我们可以创建 Function.prototype.call 的副本,并将其上下文专门设置为 String.prototype.toUpperCase。我们现在有以下

a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))

问题三

最后一个问题是String.prototype.replace()将向其替换函数传递几个参数。但是,Function.prototype.apply()期望第二个参数是一个数组,而是获取一个字符串或数字(取决于您是否使用捕获组)。这将导致无效参数列表错误。

解决方案3

幸运的是,我们可以简单地将Function.prototype.call()(它接受任意数量的参数,没有类型限制)替换Function.prototype.apply()我们现在已经到达了工作代码!

a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))

脱落字节!

没有人愿意多次输入原型相反,我们将利用这样一个事实,即我们拥有通过继承引用相同方法的对象。String 构造函数是一个函数,它继承自 Function 的原型。这意味着我们可以在 String.call 中替换Function.prototype.call(实际上我们可以使用 Date.call 来节省更多字节,但语义更少)。

我们还可以利用我们的变量“a”,因为它的原型包含对String.prototype.toUpperCase的引用,我们可以将其替换为 a.toUpperCase。正是上述 3 种解决方案和这些字节节省措施的组合,这就是我们获得本文顶部代码的方式。

从理智上讲,这是一个很好的解决方案,因为它显示/教了关于 javascript 函数的一两件事。但我和劳伦斯一样,在实践中它太模糊而无法实际使用。还是很酷的。
2021-03-16 02:57:46
您保存了 8 个字符,同时以这样一种方式模糊了代码,以至于需要对更明显的解决方案进行一页解释。我不相信这是一场胜利。
2021-03-25 02:57:46

旧帖子,但值得为其他使用更受限制的 RegEx 用例扩展 @ChaosPandion 答案。例如,确保(f)使用特定格式或捕获组环绕/z(f)oo/

> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.

我只想强调function使查找特定格式和替换格式中的捕获组的两个参数

我认为您的替换功能有误,但请检查一下。我认为应该是return $0.replace($0, $1.toUpperCase())$0第一个参数在哪里
2021-03-13 02:57:46
这是一个简单的字符串替换字符串。所以 f 到 F 是正确的。
2021-03-22 02:57:46
如果您想替换括号中的内容,这真的很有帮助!
2021-03-28 02:57:46
谢谢!之前的帖子似乎已经回答了为什么OP 的代码不起作用的问题,同时完全跳过了对我来说似乎真正的重点——替换匹配组!
2021-04-11 02:57:46

我们为什么不查一下定义呢?

如果我们写:

a.replace(/(f)/, x => x.toUpperCase())

我们不妨说:

a.replace('f','F')

更糟糕的是,我怀疑没有人意识到他们的例子之所以有效,只是因为他们用括号捕获了整个正则表达式如果查看定义,传递给replacer函数的第一个参数实际上是整个匹配的模式,而不是您用括号捕获的模式:

function replacer(match, p1, p2, p3, offset, string)

如果要使用箭头函数表示法:

a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
恕我直言,这是最简单和最优雅的解决方案。
2021-04-11 02:57:46

解决方案

a.replace(/(f)/,(m,g)=>g.toUpperCase())  

使用正则/(f)/g表达式替换所有出现的组代码中的问题:String.prototype.toUpperCase.apply("$1")"$1".toUpperCase()给出"$1"(自己在控制台中尝试) - 所以它不会改变任何东西,实际上你调用了两次a.replace( /(f)/, "$1")(也不会改变任何东西)。