Как получить данные о совпадении для всех вхождений регулярного выражения Ruby в строке? - PullRequest
42 голосов
/ 24 июля 2011

Мне нужно MatchData для каждого вхождения регулярного выражения в строке.Это отличается от метода сканирования, предложенного в Совпадение всех вхождений регулярного выражения , поскольку это дает мне только массив строк (мне нужны полные 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)

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

Ответы [ 5 ]

66 голосов
/ 24 июля 2011

Вы хотите

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

, что дает вам

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

"Уловка", как вы видите, состоит в том, чтобы создать перечислитель для получения каждого last_match.

9 голосов
/ 24 июля 2011

Мое текущее решение состоит в том, чтобы добавить метод each_match в регулярное выражение:

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

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

7 голосов
/ 02 февраля 2013

Я положу его здесь на всякий случай, чтобы сделать код доступным через соответствующий поиск Google:

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

Результат такой же, как требуется:

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

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

1 голос
/ 23 ноября 2017

Я удивлен, что никто не упомянул удивительный 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, но он дает вам основанный на индексе интерфейс в строке.

0 голосов
/ 23 ноября 2017
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">]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...