【问题标题】:Replace a Regex capture group with uppercase in Javascript在 Javascript 中用大写替换正则表达式捕获组
【发布时间】:2011-09-02 19:55:10
【问题描述】:

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

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

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

【问题讨论】:

  • @Erik 不会删除问题的组成部分。我想知道为什么我的代码也失败了。
  • 埃文,我以为我尊重你的问题。我只删除了似乎不必要的东西。由于您提供了您正在尝试的代码,并且它显然不起作用,因此人们隐含地知道您需要解释原因而无需您这么说(并且尴尬)。只是想帮忙! :)
  • 埃文,这样更好吗?我不是故意惹恼的。如果您再次回滚,我将不再编辑,但您至少可以保留标题和标签编辑吗?
  • 从技术上讲,我根本没有使用 Javascript,我使用的是 v8 (ECMAScript)。但是,我想大多数搜索这个的人都会寻找 JavaScript,所以我很擅长。
  • 如果您认为属于这些标签,请随时添加回标签。

标签: javascript regex replace uppercase


【解决方案1】:

您可以将函数传递给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:请看我的回答。
  • 啊,我明白你的意思了,我正在大写“\$1”。不是替换的伏都教的结果显然是用$1 代替第一个捕获组。
  • @EvanCarroll 详细解释了为什么您的初始代码不起作用以及如何让它起作用,请参阅下面的答案。
  • 总体而言无关紧要,但不需要捕获组 f (f),因为 v 正在引用组 0。阅读本文的人请注意。在快速基准测试中,每个捕获组都将正则表达式减慢了 5%。
【解决方案2】:

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

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)

这显然不是我们想要的。它怎么知道在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))

问题 3

最后一个问题是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 种解决方案和这些字节节省措施的组合,我们才获得了本文顶部的代码。

【讨论】:

  • 您保存了 8 个字符,同时以这样一种方式模糊了代码,以至于需要一页解释而不是更明显的解决方案。我不相信这是一场胜利。
  • 从理论上讲,这是一个很好的解决方案,因为它可以展示/教授一两件关于 javascript 函数的事情。但我同意劳伦斯的观点,在实践中它太模糊而无法实际使用。还是很酷。
【解决方案3】:

旧帖子,但值得将 @ChaosPandion 的答案扩展到具有更多限制 RegEx 的其他用例。例如。确保(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 的两个参数使得查找特定格式和替换格式内的捕获组成为可能。

【讨论】:

  • 谢谢!之前的帖子似乎已经回答了为什么 OP 的代码不起作用的问题,同时完全跳过了对我来说真正的意义——替换匹配组!
  • 我认为您的替换功能有错误,但请检查我。我觉得应该是return $0.replace($0, $1.toUpperCase()),其中$0是第一个参数
  • 这是一个简单的字符串替换字符串。所以 f 到 F 是正确的。
  • 如果您想替换括号中的内容,这真的很有帮助!
【解决方案4】:

解决方案

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

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

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

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

【讨论】:

    【解决方案5】:

    给定一个包含属性、值的字典(对象,在本例中为 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);

    【讨论】:

      【解决方案6】:

      我们为什么不直接查一下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()
      

      【讨论】:

      • 恕我直言,这是最简单、最优雅的解决方案。
      【解决方案7】:

      在将字符串从 CamelCase 转换为 bash_case 的情况下(即:对于文件名),使用带有三元运算符的回调。

      在第一个(左)替换 arg 中使用 regexp () 选择的捕获组被发送到第二个(右)arg,它是一个回调函数。 x 和 y 给出了捕获的字符串(不知道为什么 2 次!)和 index(第三个)给出了引用字符串中捕获组的开头的索引。 因此,可以使用三元运算符不要将_ 放在第一次出现。

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

      【讨论】:

        猜你喜欢
        • 2015-03-25
        • 2019-01-27
        • 1970-01-01
        • 2013-06-27
        • 1970-01-01
        • 2014-06-19
        • 2010-11-19
        • 1970-01-01
        相关资源
        最近更新 更多