【问题标题】:How to replace captured groups only?如何仅替换捕获的组?
【发布时间】:2011-04-26 16:15:36
【问题描述】:

字符串前后都有 HTML 代码:

name="some_text_0_some_text"

我想用类似的东西替换0!NEW_ID!

所以我做了一个简单的正则表达式:

.*name="\w+(\d+)\w+".*

但我不知道如何专门替换捕获的块。

有没有办法将捕获的结果(如 ($1))替换为其他字符串?

结果是:

name="some_text_!NEW_ID!_some_text"

【问题讨论】:

    标签: javascript regex


    【解决方案1】:

    解决方案是为前后文本添加捕获:

    str.replace(/(.*name="\w+)(\d+)(\w+".*)/, "$1!NEW_ID!$3")
    

    【讨论】:

    • 来自未来的问候!您的解决方案看起来非常整洁。你能解释一下你的答案吗?
    • 括号用于创建“组”,然后分配一个base-1索引,可以用$替换访问,所以第一个单词(\w+)在一个组中,并变为$1,中间部分(\d+) 是第二组,(但在替换中被忽略),第三组是$3。因此,当您给出"$1!new_ID!$3" 的替换字符串时,$1 和 $3 会自动替换为第一组和第三组,从而允许将第二组替换为新字符串,并保持其周围的文本。
    • 话虽如此,虽然我了解它的工作原理,但我希望有一个更优雅的解决方案 >。
    • 1) 你甚至不需要捕获 \d+ 2) 你为什么说它不优雅?捕获是为了保留东西,而不是扔掉它。你想要保留的是 AROUND \d+,所以捕捉这些周围的部分真的很有意义(并且足够优雅)。
    • 不错的解决方案。如果我们想使用捕获组作为转换的基础来替换捕获组怎么办?有没有同样优雅的解决方案来做到这一点?目前我将捕获的组存储在一个列表中,循环它们,并在每次迭代时用转换后的值替换捕获组
    【解决方案2】:

    对 Matthew 的回答的一点改进可能是前瞻而不是最后一个捕获组:

    .replace(/(\w+)(\d+)(?=\w+)/, "$1!NEW_ID!");
    

    或者您可以按小数拆分并使用您的新 ID 加入,如下所示:

    .split(/\d+/).join("!NEW_ID!");
    

    此处的示例/基准测试:https://codepen.io/jogai/full/oyNXBX

    【讨论】:

      【解决方案3】:

      一个更简单的选择是只捕获数字并替换它们。

      const name = 'preceding_text_0_following_text';
      const matcher = /(\d+)/;
      
      // Replace with whatever you would like
      const newName = name.replace(matcher, 'NEW_STUFF');
      console.log("Full replace", newName);
      
      // Perform work on the match and replace using a function
      // In this case increment it using an arrow function
      const incrementedName = name.replace(matcher, (match) => ++match);
      console.log("Increment", incrementedName);

      资源

      【讨论】:

        【解决方案4】:

        既然 Javascript 具有后向功能(截至 ES2018),在较新的环境中,您可以在此类情况下完全避免使用组。相反,向后看你正在捕获的组之前的内容,并向前看之后的内容,并替换为 just !NEW_ID!:

        const str = 'name="some_text_0_some_text"';
        console.log(
          str.replace(/(?<=name="\w+)\d+(?=\w+")/, '!NEW_ID!')
        );

        使用这种方法,完全匹配只有需要替换的部分。

        • (?&lt;=name="\w+) - 查找name=",后跟单词字符(幸运的是,在 Javascript 中查找的宽度不必固定!)
        • \d+ - 匹配一个或多个数字 - 模式中唯一不在环视​​中的部分,字符串中唯一将出现在结果匹配中的部分
        • (?=\w+") - 先行查找单词字符,后跟" `

        请记住,lookbehind 是相当新的。它适用于现代版本的 V8(包括 Chrome、Opera 和 Node),但not in most other environments,至少现在还不行。因此,虽然您可以在 Node 和您自己的浏览器中可靠地使用lookbehind(如果它在现代版本的 V8 上运行),但随机客户端(例如在公共网站上)还不足以支持它。

        【讨论】:

        • 刚刚进行了快速计时测试,输入的重要性令人印象深刻:jsfiddle.net/60neyop5
        • 但是,例如,如果我想提取数字、倍数并“放回去”,我还必须分组 \d+,对吧?
        • @MoshFeu 使用替换函数并使用整个匹配,数字:用match =&gt; match * 2 替换第二个参数。数字仍然是整个匹配,所以不需要组
        • 感谢分享。浏览器支持约为 75%,最明显的是 iOS Safari 缺少:caniuse.com/js-regexp-lookbehind
        【解决方案5】:

        使用两个捕获组也是可能的;我还会在数字前后添加两个破折号,作为附加的左右边界,修改后的表达式看起来像:

        (.*name=".+_)\d+(_[^"]+".*)
        

        const regex = /(.*name=".+_)\d+(_[^"]+".*)/g;
        const str = `some_data_before name="some_text_0_some_text" and then some_data after`;
        const subst = `$1!NEW_ID!$2`;
        const result = str.replace(regex, subst);
        console.log(result);

        如果您想探索/简化/修改表达式,它已经 在右上角的面板上进行了解释 regex101.com。如果你愿意,你 也可以在this link看,怎么搭配 针对一些样本输入。


        正则表达式电路

        jex.im 可视化正则表达式:

        【讨论】:

          【解决方案6】:
          "some_text_0_some_text".replace(/(?=\w+)\d+(?=\w+)/, '!NEW_ID!')
          

          结果是

          some_text_!NEW_ID!_some_text

          const regExp = /(?=\w+)\d+(?=\w+)/;
          const newID = '!NEW_ID!';
          const str = 'some_text_0_some_text';
          const result = str.replace(regExp, newID);
          
          console.log(result);

          x(?=y) 在 JS 正则表达式中

          仅当“x”后跟“y”时才匹配“x”。例如,/Jack(?=Sprat)/ 仅在其后跟“Sprat”时才匹配“Jack”。 /Jack(?=Sprat|Frost)/ 仅在“Sprat”或“Frost”后跟“Jack”匹配。但是,“Sprat”和“Frost”都不是匹配结果的一部分。

          details

          【讨论】:

            【解决方案7】:

            如果您使用的是 python,则可以使用 Match.expand() 方法在 re.sub 中使用反斜杠替换。这意味着您不需要捕获整个字符串。一个例子如下:

            import re
            
            in_str = '<h1> this is valid html</h1>name="some_text_0_some_text"'
            use_reg = 'name="(\w+)(\d+)(\w+)"'
            replace_str = r"\1!NEW_ID!\3"
            
            def find_with_replace_option(use_str, use_reg, to_str):
            """ Find matches of the regex use_reg in the string use_str. Return
                to_str if there are any matches. to_str may contain backslash 
                substitution.
            """
                result_list = []
                for match in re.finditer(use_reg,use_str):
                    result = match.expand(to_str)
                    result_list.append(result)
                return result_list
            print(find_with_replace_option(in_str,use_reg,replace_str))
            

            这里的正则表达式的工作方式如下:第一部分“some_text_”在第一组中捕获,零在第二组中捕获,最后一部分“_some_text”成为第三组。

            replace_str 指定输出应包含第一组,后跟“!NEW_ID!”其次是第三组。

            结果是 some_text_!NEW_ID!_some_text 符合预期

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-10-13
              • 1970-01-01
              • 2011-02-24
              • 1970-01-01
              • 1970-01-01
              • 2018-04-04
              相关资源
              最近更新 更多