【问题标题】:gsub partial replacegsub 部分替换
【发布时间】:2010-09-12 05:48:07
【问题描述】:

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

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

所以我得到:&lt;--MARKER_START--&gt;replace_text&lt;--MARKER_END--&gt;

我知道我可以在替换表达式中重复整个 MARKER_STARTMARKER_END 块,但我认为应该有更简单的方法来做到这一点。

【问题讨论】:

    标签: ruby regex


    【解决方案1】:

    您可以使用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 中发生的是 ?&lt;= 导致它崩溃,因为它不理解后向断言。对于那部分,您必须回退到使用反向引用 - 例如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"
    

    第二个解释:

    零宽度前瞻性断言听起来像是一大堆书呆子的困惑。

    “look-ahead-assertion”实际上的意思是“只有匹配,如果我们正在寻找的东西,后面跟着其他东西。

    例如,只匹配一个数字,如果它后跟一个 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
    

    如您所见,这是检查我们的&lt;--MARKER END--&gt; 的理想选择,但我们需要&lt;--MARKER START--&gt; 能够说“仅匹配,如果我们正在寻找的东西跟随其他东西”。这称为后视断言,由于某种奇怪的原因,ruby 1.8 没有这种断言。

    希望这是有道理的:-)

    PS:为什么使用前瞻断言而不是反向引用?如果您使用前瞻,您实际上并没有替换 &lt;--MARKER--&gt; 位,只是替换内容。如果您使用反向引用,您将替换整个批次。我不知道这是否会对性能造成很大影响,但从编程的角度来看,这似乎是正确的做法,因为我们实际上根本不想替换标记。

    【讨论】:

    • 很好的答案,我将其保存以供将来参考:)
    【解决方案2】:

    你可以这样做:

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

    【讨论】:

    • 你真的需要一个非贪婪的 .* 匹配在中间。看我的回答
    • 没错,一般来说,非贪婪匹配会更好。我将把它留给皮埃尔,因为它可能适用于他的情况,也可能不适用。
    • 我只想补充一点,在替换文本中使用单引号 (') 很重要(就像在答案中一样)。我在使用双引号 (") 时遇到了一些错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-21
    • 2019-07-22
    • 2021-01-29
    • 2017-12-24
    • 1970-01-01
    • 2020-05-07
    相关资源
    最近更新 更多