Ruby String#scan equivalent to return MatchData
memo = []
"foo _bar_ _baz_ hashbang".scan(/_[^_]+_/) { memo << Regexp.last_match }
=> "foo _bar_ _baz_ hashbang"
memo
=> [#<MatchData "_bar_">, #<MatchData "_baz_">]
You could easily build your own by exploiting MatchData#end
and the pos
parameter of String#match
. Something like this:
def matches(s, re)
start_at = 0
matches = [ ]
while(m = s.match(re, start_at))
matches.push(m)
start_at = m.end(0)
end
matches
end
And then:
>> matches("foo _bar_ _baz_ hashbang", /_[^_]+_/)
=> [#<MatchData "_bar_">, #<MatchData "_baz_">]
>> matches("_a_b_c_", /_[^_]+_/)
=> [#<MatchData "_a_">, #<MatchData "_c_">]
>> matches("_a_b_c_", /_([^_]+)_/)
=> [#<MatchData "_a_" 1:"a">, #<MatchData "_c_" 1:"c">]
>> matches("pancakes", /_[^_]+_/)
=> []
You could monkey patch that into String if you really wanted to.
If you don't need to get MatchData
s back, here's a way using StringScanner
.
require 'strscan'
rxp = /_[^_]+_/
scanner = StringScanner.new "foo _barrrr_ _baz_ hashbang"
match_infos = []
until scanner.eos?
scanner.scan_until rxp
if scanner.matched?
match_infos << {
pos: scanner.pre_match.size,
length: scanner.matched_size,
match: scanner.matched
}
else
break
end
end
p match_infos
# [{:pos=>4, :length=>8, :match=>"_barrrr_"}, {:pos=>13, :length=>5, :match=>"_baz_"}]