Ruby String # scan эквивалентно возврату MatchData

Как указано в заголовке вопроса, существует ли метод строк Ruby, эквивалентный String # Scan, но вместо того, чтобы просто возвращать список каждого совпадения, он вернет массив из MatchData s? Например:

# Matches a set of characters between underscore pairs
"foo _bar_ _baz_ hashbang".some_method(/_[^_]+_/) #=> [#&ltMatchData "_bar_"&rt, &ltMatchData "_baz_"&rt]

Или любой способ получить тот же или похожий результат был бы хорош. Я хотел бы сделать это, чтобы найти позиции и экстенты "строк" ​​в строках Ruby, например. "goodbye и "world" внутри "до свидания" жестокого "мира".

Ответ 1

Вы можете легко создать свой собственный, используя MatchData#end и параметр pos String#match. Что-то вроде этого:

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

И затем:

>> 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", /_[^_]+_/)
=> []

Вы могли бы обезглавить патч в String, если хотите.

Ответ 2

memo = []
"foo _bar_ _baz_ hashbang".scan(/_[^_]+_/) { memo << Regexp.last_match }
 => "foo _bar_ _baz_ hashbang"
memo
 => [#<MatchData "_bar_">, #<MatchData "_baz_">]

Ответ 3

Если вам не нужно возвращать MatchData назад, используйте способ 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_"}]