Ruby 1.9.3和2.0.0中的正则表达式负前瞻差异

6
我需要匹配以 "bar" 结尾但不以 "foo" 开头的数组成员,并将结果放入新数组中。
查看1.9.3和2.0.0文档,它们似乎支持相同语法的负向先行断言。负向先行断言在Ruby 2.0.0中按预期工作,但在Ruby 1.9.3中似乎不起作用。
["foo a.bar", "b.bar"].grep(/(?!^foo\s).*\.bar$/)
# => ["b.bar"]              (ruby 2.0.0)
# => ["foo a.bar", "b.bar"] (ruby 1.9.3)

这个基础设施上的 Ruby 版本将在 4 个月内升级,但早期更改版本不可行。我应该如何使其在 1.9.3 中运行,并最好在 2.0 中继续工作?


我认为这是Ruby 2.0的错误,而不是Ruby 1.9.3的。 - sawa
1
我刚刚向Ruby核心团队报告了这个错误。 - sawa
3个回答

8
最好使用这个,看起来更有说服力:
matched = array.grep(/^(?!foo\s).*\.bar$/)

NOT开头不是foo

这在2.1.1和1.9.3中都有效。

只有当您想要查看我所做的内容时才需要:

# ruby-1.9.3-p362
array = ["foo a.bar", "b.bar"] 
# => ["foo a.bar", "b.bar"] 
matched = array.grep(/(?!^foo\s).*\.bar$/)
# => ["foo a.bar", "b.bar"] 
matched = array.grep(/^(?!foo\s).*\.bar$/)
# => ["b.bar"] 
matched = array.grep(/(?!^foo\s).*\.bar$/)
# => ["foo a.bar", "b.bar"]

# ruby-2.1.1
array = ["foo a.bar", "b.bar"] 
# => ["foo a.bar", "b.bar"] 
matched = array.grep(/(?!^foo\s).*\.bar$/)
# => ["b.bar"] 
matched = array.grep(/^(?!foo\s).*\.bar$/)
# => ["b.bar"] 

4

如果在生产服务器上使用的ruby版本存在问题,一个简单的解决方案就是不要使用负向先行断言。如果你的示例足够特殊,你可以使用 select 和便捷的字符串方法:

array.select {|str| !str.starts_with?('foo') && str.ends_with?('bar') }

我也希望这个过程更快一些(尤其是如果你优化为首先匹配最低频率的情况下)。虽然楼主可能简化了问题,实际上使用了模式匹配而不是"foo"和"bar"。 - Neil Slater

3
你的正则表达式有问题,而不是Ruby的问题。Ruby 2似乎更加宽容。
开始锚点(^)应该在前瞻之前,而不是在里面。当它无法在行首匹配foo时,正则表达式引擎向前移动一个位置并尝试再次匹配。它不再处于字符串的开头,因此^不匹配,否定前瞻报告成功,然后正则表达式匹配oo a.bar。(这是一个非常常见的错误。)

我认为你是正确的,Ruby 2.0 错误的可能性比 Ruby 1.9.3 更大。 - sawa
这是一个关于为什么它也失败的天才解释! - SidOfc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接