Comment faire correspondre toutes les occurrences d'une expression régulière


586

Existe-t-il un moyen rapide de trouver chaque correspondance d'une expression régulière dans Ruby? J'ai parcouru l'objet Regex dans la Ruby STL et cherché sur Google en vain.


3
J'ai lu ceci est comment puis-je rechercher une chaîne pour tous les modèles d'expression
régulière

Réponses:


821

L'utilisation scandevrait faire l'affaire:

string.scan(/regex/)

9
Mais qu'en est-il de cette affaire? "Match me!". scan (/.../) = ["mat", "ch" "me!" ], mais toutes les occurrences de /.../ seraient ["mat", "atc", "tch", "ch", ...]
Michael Dickens

13
Ce ne serait pas le cas. /.../ est une expression rationnelle gourmande normale. Il ne reviendra pas sur le contenu correspondant. vous pouvez essayer d'utiliser une expression rationnelle paresseuse, mais même cela ne suffira probablement pas. jetez un oeil à la doc regexp ruby-doc.org/core-1.9.3/Regexp.html pour exprimer correctement votre regexp :)
Jean

49
cela ressemble à un Ruby WTF ... pourquoi est-ce sur String au lieu de Regexp avec les autres trucs regexp? Il n'est même pas mentionné nulle part sur les documents pour Regexp
Anentropic

9
Je suppose que c'est parce qu'il est défini et appelé sur String et non sur Regex ... Mais cela a du sens. Vous pouvez écrire une expression régulière pour capturer toutes les correspondances à l'aide de la correspondance Regex # et parcourir les groupes capturés. Ici, vous écrivez une fonction de correspondance partielle et souhaitez qu'elle soit appliquée plusieurs fois sur une chaîne donnée, ce n'est pas la responsabilité de Regexp. Je vous suggère de vérifier l'implémentation du scan pour une meilleure compréhension: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Jean

9
@MichaelDickens: Dans ce cas, vous pouvez utiliser /(?=(...))/.
Konrad Borowski

67

Pour trouver toutes les chaînes correspondantes, utilisez la scanméthode String .

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

Si vous le souhaitez, MatchDataqui est le type de l'objet renvoyé par la matchméthode Regexp , utilisez:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

L'avantage d'utiliser MatchDataest que vous pouvez utiliser des méthodes telles que offset:

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

Consultez ces questions si vous souhaitez en savoir plus:

La lecture sur les variables spéciales $&, $', $1, $2Ruby sera utile aussi.


12

si vous avez une expression rationnelle avec des groupes:

str="A 54mpl3 string w1th 7 numbers scatter3r ar0und"
re=/(\d+)[m-t]/

vous pouvez utiliser la scanméthode de String pour trouver des groupes correspondants:

str.scan re
#> [["54"], ["1"], ["3"]]

Pour trouver le motif correspondant:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]

str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]est plus idiomatique questr.to_enum(:scan,re).map {$&}
le Tin Man

Vous avez peut-être mal compris. L'expression régulière de l'exemple d'un utilisateur auquel j'ai répondu était: /(\d+)[m-t]/ne pas /\d+[m-t]/écrire: re = /(\d+)[m-t]/; str.scan(re)c'est la même chose str.scan(/(\d+)[mt]/)mais j'obtiens #> [["" 54 "], [" 1 "], [" 3 "]]et non "54m", "1t", "3r"]La question était: si j'ai une expression régulière avec un groupe et que je veux capturer tous les modèles sans changer le régulier expression (quitter le groupe), comment faire? En ce sens, une solution possible, quoique un peu cryptique et difficile à lire, était:str.to_enum(:scan,re).map {$&}
MVP

-1

Vous pouvez utiliser string.scan(your_regex).flatten. Si votre expression régulière contient des groupes, elle reviendra dans un seul tableau simple.

string = "A 54mpl3 string w1th 7 numbers scatter3r ar0und"
your_regex = /(\d+)[m-t]/
string.scan(your_regex).flatten
=> ["54", "1", "3"]

Regex peut également être un groupe nommé.

string = 'group_photo.jpg'
regex = /\A(?<name>.*)\.(?<ext>.*)\z/
string.scan(regex).flatten

Vous pouvez également utiliser gsub, c'est juste une autre façon si vous voulez MatchData.

str.gsub(/\d/).map{ Regexp.last_match }

Supprimez le regroupement your_regex = /(\d+)[m-t]/et vous n'aurez pas besoin de l'utiliser flatten. Votre dernier exemple utilise last_matchqui, dans ce cas, est probablement sûr, mais est global et pourrait éventuellement être écrasé si une expression rationnelle était mise en correspondance avant l'appel last_match. Au lieu de cela, il est probablement plus sûr à utiliser string.match(regex).captures # => ["group_photo", "jpg"]ou string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]comme indiqué dans d'autres réponses, selon le modèle et les besoins.
The Tin Man
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.