web-dev-qa-db-fra.com

Ruby Groupe correspondant aux expressions rationnelles, affectez des variables sur une ligne

J'essaie actuellement de rexp une chaîne en plusieurs variables. Exemple de chaîne:

ryan_string = "RyanOnRails: This is a test"

Je l'ai associé à cette expression rationnelle, avec 3 groupes:

ryan_group = ryan_string.scan(/(^.*)(:)(.*)/i)

Maintenant, pour accéder à chaque groupe, je dois faire quelque chose comme ceci:

ryan_group[0][0] (first group) RyanOnRails
ryan_group[0][1] (second group) :
ryan_group[0][2] (third group) This is a test

Cela semble assez ridicule et j'ai l'impression que je fais quelque chose de mal. Je pense pouvoir faire quelque chose comme ceci:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)

Est-ce possible? Ou y a-t-il un meilleur moyen que ce que je fais?

112
ryanjones

Vous ne voulez pas scan pour cela, cela n'a pas beaucoup de sens. Vous pouvez utiliser String#match Pour renvoyer un objet MatchData. Vous pouvez ensuite appeler #captures Pour renvoyer un tableau de captures. Quelque chose comme ça:

#!/usr/bin/env Ruby

string = "RyanOnRails: This is a test"
one, two, three = string.match(/(^.*)(:)(.*)/i).captures

p one   #=> "RyanOnRails"
p two   #=> ":"
p three #=> " This is a test"

Sachez que si aucune correspondance n'est trouvée, String#match Renverra zéro, ce qui pourrait fonctionner mieux:

if match = string.match(/(^.*)(:)(.*)/i)
  one, two, three = match.captures
end

Bien que scan ait peu de sens pour cela. Il fait toujours le travail, il vous suffit d’aplatir d’abord le tableau retourné. one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten

183
Lee Jarvis

Vous pouvez utiliser Match ou = ~, ce qui vous donnerait une seule correspondance et vous pourrez accéder aux données de correspondance de la même manière ou simplement utiliser les variables de correspondance spéciales $ 1, $ 2, $ 3.

Quelque chose comme:

if ryan_string =~ /(^.*)(:)(.*)/i
   first = $1
   third = $3
end
41
Rado

Vous pouvez nommer vos matchs capturés

string = "RyanOnRails: This is a test"
/(?<one>^.*)(?<two>:)(?<three>.*)/i =~ string
puts one, two, three

Cela ne fonctionne pas si vous inversez l'ordre de chaîne et l'expression régulière.

27
toonsend

Vous devez décider si c'est une bonne idée, mais Ruby regexp peut (automagiquement) définir des variables locales pour vous!

Je ne sais pas encore si cette fonctionnalité est géniale ou tout simplement folle, mais votre expression régulière peut définir des variables locales.

ryan_string = "RyanOnRails: This is a test"
/^(?<webframework>.*)(?<colon>:)(?<rest>)/ =~ ryan_string
# This defined three variables for you. Crazy, but true.
webframework # => "RyanOnRails"
puts "W: #{webframework} , C: #{colon}, R: #{rest}"

(Jetez un oeil à http://Ruby-doc.org/core-2.1.1/Regexp.html , recherchez "variable locale").

Remarque: Comme indiqué dans un commentaire, je constate qu'il existe une réponse similaire et antérieure à cette question de @toonsend ( https: //stackoverflow.com/a/21412455 ). Je ne pense pas que je "volais", mais si vous voulez être juste avec les louanges et honorer la première réponse, sentez-vous libre :) J'espère qu'aucun animal n'a été blessé.

6
Felix

scan() trouvera toutes les correspondances non répétitives de l'expression rationnelle dans votre chaîne. Ainsi, au lieu de renvoyer un tableau de vos groupes comme vous semblez l'attendre, il renvoie un tableau de tableaux.

Il vaut probablement mieux utiliser match(), puis obtenir le tableau de captures à l'aide de MatchData#captures:

g1, g2, g3 = ryan_string.match(/(^.*)(:)(.*)/i).captures

Cependant, vous pouvez aussi le faire avec scan() si vous voulez:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)[0]
3
Andrew Clark