Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

regex negative look-ahead in ruby 1.9.3 vs 2.0.0

I need to match members of an array that end with "bar" but do not start with "foo", and put the result in a new array.

Looking at the docs for 1.9.3 and 2.0.0, they seem to support negative look-ahead with the same syntax. Negative look-ahead works as I expect in ruby 2.0.0 but doesn't seem to work in 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)

The ruby version on this infrastructure will be upgraded in 4 months, but changing the version sooner isn't an option. How can I make this work in 1.9.3 and preferably continue to work in 2.0?

like image 544
mdur Avatar asked Apr 11 '14 05:04

mdur


3 Answers

Better use this, which looks more convincing:

matched = array.grep(/^(?!foo\s).*\.bar$/)

NOT starting with foo

this will work in both 2.1.1 and 1.9.3

only if you want to see what I did:

# 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"] 
like image 64
aelor Avatar answered Oct 18 '22 15:10

aelor


Simple solution would be not to use negative look-ahead, if it seems problematic in ruby version you are bound to on production servers. If your example is particular enough you could use select and convenient String methods:

array.select {|str| !str.starts_with?('foo') && str.ends_with?('bar') }
like image 34
samuil Avatar answered Oct 18 '22 15:10

samuil


It's your regex that's faulty, not Ruby. Ruby 2 seems to be a little more forgiving, is all.

The start anchor (^) should be before the lookahead, not in it. When it fails to match foo at the beginning of the line, the regex engine bumps forward one position and tries again. It's not at the start of the string any more, so ^ doesn't match, the negative lookahead reports success, and the regex matches oo a.bar. (This is a very common mistake.)

like image 45
Alan Moore Avatar answered Oct 18 '22 15:10

Alan Moore