web-dev-qa-db-fra.com

Comment obtenir les données de correspondance pour toutes les occurrences d'une expression régulière Ruby dans une chaîne?

J'ai besoin de MatchData pour chaque occurrence d'une expression régulière dans une chaîne. Ceci est différent de la méthode d'analyse suggérée dans Match toutes les occurrences d'un regex , car cela ne me donne qu'un tableau de chaînes (j'ai besoin de l'intégralité de MatchData pour obtenir les informations de début et de fin, etc.).

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)

Je soupçonne qu'il y a une méthode que j'ai négligée. Suggestions?

41
Joshua Flanagan

Tu veux 

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

qui te donne

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

Le "truc" consiste, comme vous le voyez, à construire un énumérateur afin d’obtenir chaque last_match.

65
i-blis

Ma solution actuelle consiste à ajouter une méthode 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

Maintenant je peux faire:

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

Dis-moi, il y a un meilleur moyen.

8
Joshua Flanagan

Je le mettrai ici au cas où le code serait disponible via la recherche Google correspondante:

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

Le résultat est tel que demandé:

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

Explication détaillée .

6
Aleksei Matiushkin

Je suis surpris que personne n'ait mentionné l'étonnant StringScanner class inclus dans la bibliothèque standard de 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

Non, cela ne vous donne pas les objets MatchData, mais cela vous donne une interface basée sur un index dans la chaîne. 

1
mwp
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">]
0
Lyndon S