如何仅替换捕获的组?
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!')
);
使用这种方法,完全匹配只有需要替换的部分。
(?<=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 => 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:无法用第一个捕获的组替换源文本