简体   繁体   English

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

[英]Replace a Regex capture group with uppercase in Javascript

I'd like to know how to replace a capture group with its uppercase in JavaScript.我想知道如何在 JavaScript 中用大写字母替换捕获组。 Here's a simplified version of what I've tried so far that's not working:这是迄今为止我尝试过的但不起作用的简化版本:

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

Would you explain what's wrong with this code?你能解释一下这段代码有什么问题吗?

You can pass a function to replace .您可以通过 function 来replace .

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

Explanation解释

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

In this example you pass a string to the replace function.在此示例中,您将字符串传递给替换 function。 Since you are using the special replace syntax ($N grabs the Nth capture) you are simply giving the same value.由于您使用的是特殊的替换语法($N 获取第 N 次捕获) ,因此您只是给出了相同的值。 The toUpperCase is actually deceiving because you are only making the replace string upper case (Which is somewhat pointless because the $ and one 1 characters have no upper case so the return value will still be "$1" ) . toUpperCase实际上具有欺骗性,因为您只是将替换字符串设为大写(这有点毫无意义,因为$和一个1字符没有大写,因此返回值仍然是"$1"

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

Believe it or not the semantics of this expression are exactly the same.信不信由你,这个表达式的语义是完全一样的。

I know I'm late to the party but here is a shorter method that is more along the lines of your initial attempts.我知道我迟到了,但这里有一个更短的方法,更符合你最初的尝试。

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

So where did you go wrong and what is this new voodoo?那么你在哪里 go 错了,这个新的巫毒是什么?

Problem 1问题 1

As stated before, you were attempting to pass the results of a called method as the second parameter of String.prototype.replace() , when instead you ought to be passing a reference to a function如前所述,您试图将调用方法的结果作为String.prototype.replace()的第二个参数传递,而您应该传递对 function 的引用

Solution 1解决方案 1

That's easy enough to solve.这很容易解决。 Simply removing the parameters and parentheses will give us a reference rather than executing the function.简单地去掉参数和括号会给我们一个参考而不是执行function。

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

Problem 2问题 2

If you attempt to run the code now you will get an error stating that undefined is not a function and therefore cannot be called.如果您现在尝试运行代码,您将收到一条错误消息,指出 undefined 不是 function,因此无法调用。 This is because String.prototype.toUpperCase.apply is actually a reference to Function.prototype.apply() via JavaScript's prototypical inheritance.这是因为 String.prototype.toUpperCase.apply 实际上是通过 JavaScript 的原型 inheritance 对Function.prototype.apply()的引用。 So what we are actually doing looks more like this所以我们实际上在做的看起来更像这样

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

Which is obviously not what we have intended.这显然不是我们的本意。 How does it know to run Function.prototype.apply() on String.prototype.toUpperCase() ?它怎么知道在String.prototype.toUpperCase( ) 上运行Function.prototype.apply( ) ?

Solution 2解决方案 2

Using Function.prototype.bind() we can create a copy of Function.prototype.call with its context specifically set to String.prototype.toUpperCase.使用Function.prototype.bind()我们可以创建 Function.prototype.call 的副本,其上下文专门设置为 String.prototype.toUpperCase。 We now have the following我们现在有以下

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

Problem 3问题 3

The last issue is that String.prototype.replace() will pass several arguments to its replacement function.最后一个问题是String.prototype.replace()会将几个 arguments 传递给它的替换 function。 However, Function.prototype.apply() expects the second parameter to be an array but instead gets either a string or number (depending on if you use capture groups or not).但是, Function.prototype.apply()期望第二个参数是一个数组,而是获取一个字符串或数字(取决于您是否使用捕获组)。 This would cause an invalid argument list error.这将导致无效的参数列表错误。

Solution 3解决方案 3

Luckily, we can simply substitute in Function.prototype.call() (which accepts any number of arguments, none of which have type restrictions) for Function.prototype.apply() .幸运的是,我们可以简单地将Function.prototype.call() (它接受任意数量的 arguments,其中没有类型限制)替换为Function.prototype.app We have now arrived at working code!我们现在已经到了工作代码!

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

Shedding bytes!脱落字节!

Nobody wants to type prototype a bunch of times.没有人愿意多次键入原型 Instead we'll leverage the fact that we have objects that reference the same methods via inheritance.相反,我们将利用通过 inheritance 引用相同方法的对象这一事实。 The String constructor, being a function, inherits from Function's prototype. String 构造函数是 function,继承自 Function 的原型。 This means that we can substitute in String.call for Function.prototype.call (actually we can use Date.call to save even more bytes but that's less semantic).这意味着我们可以在 String.call 中替换Function.prototype.call (实际上我们可以使用 Date.call 来节省更多字节,但这不太语义)。

We can also leverage our variable 'a' since it's prototype includes a reference to String.prototype.toUpperCase we can swap that out with a.toUpperCase.我们还可以利用我们的变量“a”,因为它的原型包含对String.prototype.toUpperCase的引用,我们可以将其替换为 a.toUpperCase。 It is the combination of the 3 solutions above and these byte saving measures that is how we get the code at the top of this post.正是上述 3 种解决方案和这些字节节省措施的组合,我们才获得了这篇文章顶部的代码。

Old post but it worth to extend @ChaosPandion answer for other use cases with more restricted RegEx.旧帖子,但值得扩展 @ChaosPandion 答案以适用于更多受限制的 RegEx 用例。 Eg ensure the (f) or capturing group surround with a specific format /z(f)oo/ :例如,确保(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.

I just want to highlight the two parameters of function makes finding a specific format and replacing a capturing group within the format possible.我只想强调function的两个参数使查找特定格式和替换格式中的捕获组成为可能。

Why don't we just look up the definition ?我们为什么不直接查一下定义呢?

If we write:如果我们写:

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

we might as well just say:我们不妨说:

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

Worse, I suspect nobody realises that their examples have been working only because they were capturing the whole regex with parentheses.更糟糕的是,我怀疑没有人意识到他们的示例之所以有效,只是因为他们用括号捕获了整个正则表达式 If you look at the definition , the first parameter passed to the replacer function is actually the whole matched pattern and not the pattern you captured with parentheses:如果您查看定义,传递给replacer器 function 的第一个参数实际上是整个匹配模式,而不是您用括号捕获的模式:

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

If you want to use the arrow function notation:如果要使用箭头 function 表示法:

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

SOLUTION解决方案

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

for replace all grup occurrences use /(f)/g regexp.替换所有出现的 grup 使用/(f)/g正则表达式。 The problem in your code: String.prototype.toUpperCase.apply("$1") and "$1".toUpperCase() gives "$1" (try in console by yourself) - so it not change anything and in fact you call twice a.replace( /(f)/, "$1") (which also change nothing).您的代码中的问题: String.prototype.toUpperCase.apply("$1")"$1".toUpperCase()给出了"$1" (自己在控制台中尝试) - 所以它不会改变任何东西,实际上你调用a.replace( /(f)/, "$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);

Given a dictionary (object, in this case, a Map ) of property, values, and using .bind() as described at answers给定一个字典(对象,在这种情况下,一个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);

Using a JavaScript plain object and with a function defined to get return matched property value of the object, or original string if no match is found使用 JavaScript 普通 object 并定义 function 以获取 ZA8CFDE6331BD59EB26AC96 的原始字符串匹配的返回匹配属性值

 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);

In the case of string conversion from CamelCase to bash_case (ie: for filenames), use a callback with ternary operator.在从 CamelCase 到 bash_case 的字符串转换(即:对于文件名)的情况下,使用带有三元运算符的回调。

The captured group selected with a regexp () in the first (left) replace arg is sent to the second (right) arg that is a callback function .在第一个(左)替换 arg 中使用regexp ()选择的捕获组被发送到第二个(右)arg,即回调 function x and y give the captured string (don't know why 2 times.) and index (the third one) gives the index of the beginning of the captured group in the reference string. xy给出了捕获的字符串(不知道为什么 2 次。)和 index(第三个)给出了引用字符串中捕获组的开头的索引。 Therefor a ternary operator can be used not to place _ at first occurence.因此,可以使用三元运算符在第一次出现时不放置_

 let str = 'MyStringName'; str = str.replace(/([^a-z0-9])/g, (x,y,index) => { return index?= 0. '_' + x:toLowerCase(). x;toLowerCase(); }). console;log(str);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM