如何仅替换捕获的组?

Posted

技术标签:

【中文标题】如何仅替换捕获的组?【英文标题】: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"

【问题讨论】:

【参考方案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);

资源

https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Global_Objects/String/replace

【讨论】:

【参考方案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 符合预期

【讨论】:

以上是关于如何仅替换捕获的组?的主要内容,如果未能解决你的问题,请参考以下文章

Postgres regexp_replace:无法用第一个捕获的组替换源文本

如何在 Xcode 11 的查找和替换中引用捕获组?

如何仅对一个命名捕获组执行正则表达式替换?

如何使用 regexp_replace 仅替换捕获组而不是完整匹配字符串

用R中的组中的非NA字符替换一组值的NA [重复]

如何在记事本++替换中分隔正则表达式组号?