Как получить данные соответствия для всех вхождений регулярного выражения Ruby в строку?

Мне нужно MatchData для каждого вхождения регулярного выражения в строку. Это отличается от метода сканирования, предложенного в Match All Occurrences of Regex, так как это дает мне только массив строк (мне нужно полное MatchData, чтобы начать и конечная информация и т.д.).

input = "abc12def34ghijklmno567pqrs"
numbers = /\d+/

numbers.match input # #<MatchData "12"> (only the first match)
input.scan numbers  # ["12", "34", "567"] (all matches, but only the strings)

Я подозреваю, что есть какой-то метод, который я забыл. Предложения?

Ответ 1

Вы хотите

"abc12def34ghijklmno567pqrs".to_enum(:scan, /\d+/).map { Regexp.last_match }

который дает вам

[#<MatchData "12">, #<MatchData "34">, #<MatchData "567">] 

"Трюк", как вы видите, построил перечислитель, чтобы получить каждый last_match.

Ответ 2

Мое текущее решение заключается в добавлении метода each_match в Regexp:

class Regexp
  def each_match(str)
    start = 0
    while matchdata = self.match(str, start)
      yield matchdata
      start = matchdata.end(0)
    end
  end
end

Теперь я могу сделать:

numbers.each_match input do |match|
  puts "Found #{match[0]} at #{match.begin(0)} until #{match.end(0)}"
end

Скажите, что есть лучший способ.

Ответ 3

Поместите это здесь на всякий случай, чтобы сделать код доступным через соответствующий поиск google:

input = "abc12def34ghijklmno567pqrs"
numbers = /\d+/
input.gsub(numbers) { |m| p $~ }

Результат такой же, как и ожидалось:

⇒ #<MatchData "12">
⇒ #<MatchData "34">
⇒ #<MatchData "567">

Подробное объяснение.

Ответ 4

input = "abc12def34ghijklmno567pqrs"
n = Regexp.new("\\d+")
[n.match(input)].tap { |a| a << n.match(input,a.last().end(0)+1) until a.last().nil? }[0..-2]

=> [#<MatchData "12">, #<MatchData "34">, #<MatchData "567">]

Ответ 5

Я удивлен, что никто не упомянул удивительный StringScanner класс, включенный в стандартную библиотеку Ruby:

require 'strscan'

s = StringScanner.new('abc12def34ghijklmno567pqrs')

while s.skip_until(/\d+/)
  num, offset = s.matched.to_i, [s.pos - s.matched_size, s.pos - 1]

  # ..
end

Нет, это не дает вам объекты MatchData, но это дает вам интерфейс с индексами в строку.