gsub部分替换

时间:2008-09-23 02:48:26

标签: ruby regex

我想在此表达式中仅替换括号中的组:

my_string.gsub(/<--MARKER_START-->(.)*<--MARKER_END-->/, 'replace_text')

所以我得到:<--MARKER_START-->replace_text<--MARKER_END-->

我知道我可以在替换表达式中重复整个MARKER_STARTMARKER_END块,但我认为应该有一种更简单的方法来执行此操作。

2 个答案:

答案 0 :(得分:16)

您可以使用zero width look-ahead and look-behind assertions完成此操作。

这个正则表达式应该适用于ruby 1.9以及perl和许多其他地方:

注意:ruby 1.8仅支持预见断言。您需要前瞻和后视才能正确执行此操作。

 s.gsub( /(?<=<--MARKER START-->).*?(?=<--MARKER END-->)/, 'replacement text' )

在ruby 1.8中发生的是?<=导致它崩溃,因为它不理解后面的断言。对于那部分,您必须回到使用反向引用 - 如Greig Hewgill mentions

所以你得到的是

 s.gsub( /(<--MARKER START-->).*?(?=<--MARKER END-->)/, '\1replacement text' )

解释第一:

我用(.)*替换了你的正则表达式中间的.*? - 这是非贪婪的。 如果你没有非贪婪,那么你的正则表达式将尝试尽可能多地匹配 - 如果你在一行上有2个标记,则会出错。最好通过示例说明:

"<b>One</b> Two <b>Three</b>".gsub( /<b>.*<\/b>/, 'BOLD' )
=> "BOLD"

我们真正想要的是什么:

"<b>One</b> Two <b>Three</b>".gsub( /<b>.*?<\/b>/, 'BOLD' )
=> "BOLD Two BOLD"

解释第二:

零宽度 - 前瞻 - 断言听起来像是一堆书呆子混乱。

“预见断言”实际上意味着“只有匹配,如果我们正在寻找的东西,其后是其他东西。

例如,只匹配一个数字,如果后跟一个数字。

"123F" =~ /\d(?=F)/ # will match the 3, but not the 1 or the 2

“零宽度”实际上意味着“在我们的搜索中考虑'后跟',但在进行替换或分组或类似事情时不要将其视为匹配的一部分。 使用相同的123F示例,如果我们没有使用前瞻断言,而只是这样做:

"123F" =~ /\dF/ # will match 3F, because F is considered part of the match

正如您所看到的,这是检查我们<--MARKER END-->的理想选择,但我们<--MARKER START-->所需要的是能够说“只匹配,如果我们正在寻找的东西,请关注此项其他的东西”。这被称为后视断言,红宝石1.8没有出于某种奇怪的原因......

希望有道理: - )

PS:为什么要使用先行断言而不仅仅是反向引用?如果使用lookahead,则实际上并不是替换<--MARKER-->位,而只替换内容。如果你使用反向引用,你将取代整个批次。我不知道这是否会引起很大的性能损失,但从编程的角度来看,这似乎是正确的做法,因为我们实际上根本不想更换标记。

答案 1 :(得分:14)

你可以这样做:

my_string.gsub(/(<--MARKER_START-->)(.*)(<--MARKER_END-->)/, '\1replace_text\3')