为什么Ruby gsub不会替换这种模式的第二次出现?

时间:2014-06-22 16:06:55

标签: ruby regex gsub ruby-1.8.7

我有一些代码可以从字符串中转义双引号,其中可能包含预先转义的引号; e.g:

This is a \"string"

在Ruby 1.8.7p374中使用以下代码:

string.gsub!(/([^\\])"/, '\1\"')

但是,在以下字符串上尝试时,我得到了一些有趣的边缘情况:ab""c => ab\""c。我希望它能逃脱两个引号。

这绝对不是一个大问题,但它让我很好奇 这是我的表达错误吗?一个gsub错误/功能?

(在较新的Ruby版本中,这可能很容易通过使用负面回顾来解决,但在这个版本中似乎不支持它们。)

2 个答案:

答案 0 :(得分:3)

要求匹配非\字符意味着正则表达式需要使用该字符以及引号。 gsub匹配也不能重叠。

你是正确的,后面的断言会解决这个问题。但是如果没有它,你可以在Ruby 1.8.7中有几个选择。

  1. 重复直到没有替换为止(gsub!如果没有匹配则返回nil):

    loop { break unless string.gsub!(/([^\\])"/, '\1\"') }

  2. 对于1.8.7,您不会有后瞻性断言。但是你可以反转字符串,使用 look-ahead 断言进行更改,然后将其反转:

    string = string.reverse.gsub(/"(?!\\)/, '"\\').reverse

答案 1 :(得分:2)

如果字符串的 start 中有引号,那么正则表达式也不起作用,例如"ab""c将转换为"ab\""c。其原因类似于双引号的情况。

gsub匹配b"并替换后,它会从上一场比赛继续,查看下一个",但不会查看之前消费的字符。

您可能能够在较新的Ruby版本中使用lookbehind修复您的问题,但这不会解决字符串问题的开始。解决这个问题的方法是使用\G anchor(在Ruby 1.8.7中可用),它匹配上一个匹配结束的位置或字符串的开头。因此,在非斜杠位于当前匹配开头(即"之后,您正在寻找 "刚刚匹配或这是字符串的开头)。像这样:

string.gsub!(/([^\\]|\G)"/, '\1\"')

这会将字符串"ab""c转换为\"ab\"\"c