gsub部分替换
我只想在此表达式中替换括号中的组:
my_string.gsub(/<--MARKER_START-->(.)*<--MARKER_END-->/, 'replace_text')
这样我得到:<-MARKER_START-> replace_text <-MARKER_END->
我知道我可以在替换表达式中重复整个MARKER_START
和MARKER_END
块,但是我认为应该有一个更简单的方法来执行此操作。
解决方案
我们可以执行以下操作:
my_string.gsub(/(<--MARKER_START-->)(.*)(<--MARKER_END-->)/, 'replace_text')
我们可以使用零宽度的超前声明和后向声明。
这个正则表达式应该在ruby 1.9和perl以及许多其他地方工作:
注意:ruby 1.8仅支持预定义断言。我们需要先行和后行才能正确执行此操作。
s.gsub( /(?<=<--MARKER START-->).*?(?=<--MARKER END-->)/, 'replacement text' )
在ruby 1.8中发生的事情是?<=
导致它崩溃,因为它不理解后向断言。对于那部分,我们必须回退到使用诸如Greig Hewgill提到的反向引用
所以你得到的是
s.gsub( /(<--MARKER START-->).*?(?=<--MARKER END-->)/, 'replacement 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"
解释之二:
零宽度超前断言听起来像一大堆书呆子混乱。
"先行断言"实际上的意思是"如果我们要寻找的东西,则只有匹配项之后才是其他内容。
例如,仅匹配一个数字,如果后跟一个F。
"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->
的需求是能够说"只有匹配,如果我们正在寻找的东西跟在这其他东西之后"。这称为后置断言,ruby 1.8出于某种奇怪的原因而没有。
希望有道理:-)
PS:为什么要使用前瞻性断言而不是仅使用反向引用?如果使用前瞻,实际上并没有取代<-MARKER->
位,而是仅取代了内容。如果使用反向引用,则将替换全部。我不知道这是否会对性能造成很大的影响,但是从编程的角度来看,这似乎是正确的选择,因为我们实际上根本不想替换标记。