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

时间:2011-05-26 17:51:48

标签: javascript regex replace uppercase

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

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

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

6 个答案:

答案 0 :(得分:136)

您可以将功能传递给replace

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

<强>解释

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

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

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

信不信由这个表达式的语义完全相同。

答案 1 :(得分:11)

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

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

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

问题1

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

解决方案1 ​​

这很容易解决。简单地删除参数和括号将为我们提供参考而不是执行函数。

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

问题2

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

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

这显然不是我们想要的。如何在Function.prototype.apply()上运行String.prototype.toUpperCase()

解决方案2

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

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

问题3

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

解决方案3

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

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

Shedding bytes!

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

我们还可以利用我们的变量&#39; a&#39;因为它的原型包含对{{3}}的引用,我们可以用a.toUpperCase交换它。它是上述3种解决方案和这些节省字节的措施的组合,这就是我们如何在这篇文章的顶部获得代码。

答案 2 :(得分:5)

Old post but it worth to extend @ChaosPandion answer for other use cases with more restricted RegEx. E.g. ensure the (f) or capturing group surround with a specific format /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.

I just want to highlight the two parameters of function makes finding a specific format and replacing a capturing group within the format possible.

答案 3 :(得分:2)

解决方案

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

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

let a= "foobar";
let b= a.replace(/(f)/,x=>x.toUpperCase());
let c= a.replace(/(o)/g,x=>x.toUpperCase());

console.log("/(f)/ ", b);
console.log("/(o)/g", c);

答案 4 :(得分:0)

给出属性,值的字典(在这种情况下为Map),并按照答案中的描述使用.bind()

const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]); 
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));

console.log(str);

使用JavaScript普通对象并定义一个函数以获取对象的返回匹配属性值,如果找不到匹配项,则返回原始字符串

const regex = /([A-z0-9]+)/;
const dictionary = {
  "hello": 123,
  [Symbol("dictionary")](prop) {
    return this[prop] || prop
  }
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));

console.log(str);

答案 5 :(得分:0)

为什么我们不只是查找the definition

如果我们写:

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

我们不妨说:

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

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

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

如果要使用箭头功能符号:

a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()