Comment écrire une instruction de commutateur Ruby (cas… quand) avec regex et backreferences?

87

Je sais que je peux écrire une instruction de cas Ruby pour vérifier une correspondance avec une expression régulière. Cependant, j'aimerais utiliser les données de correspondance dans ma déclaration de retour. Quelque chose comme ce semi-pseudocode:

foo = "10/10/2011"

case foo
    when /^([0-9][0-9])/
        print "the month is #{match[1]}"
    else
        print "something else"
end

Comment puis-je y parvenir?

Merci!


Juste une note: je comprends que je n'utiliserais jamais une instruction switch pour un cas simple comme ci-dessus, mais ce n'est qu'un exemple. En réalité, ce que j'essaie de réaliser est la mise en correspondance de nombreuses expressions régulières potentielles pour une date qui peut être écrite de différentes manières, puis l'analyse avec la classe Ruby Date en conséquence.

Yuval Karmi
la source
1
Ruby Date.parse comprend de nombreux formats de date. L'as tu essayé?
raine
Bien que cela ne réponde pas à cette question, vous voudrez peut-être regarder le joyau chronique ...
DGM

Réponses:

154

Les références aux derniers groupes de correspondance regex sont toujours stockées dans des pseudo variables $1 pour $9:

case foo
when /^([0-9][0-9])/
    print "the month is #{$1}"
else
    print "something else"
end

Vous pouvez également utiliser la $LAST_MATCH_INFOpseudo variable pour accéder à l'ensemble de l' MatchDataobjet. Cela peut être utile lors de l'utilisation de captures nommées:

case foo
when /^(?<number>[0-9][0-9])/
    print "the month is #{$LAST_MATCH_INFO['number']}"
else
    print "something else"
end
Yossi
la source
1
@Yossi Avez-vous une source pour votre commentaire concernant la sécurité des threads? Je viens de faire une expérience dans ruby ​​1.8.7 qui semble indiquer qu'il est thread-safe! (Thread correspondant à une regex toutes les secondes - vérifier dans irb si les matchs locaux sont encombrés)
Joel
5
-1 Les variables $ liées aux expressions régulières ne sont pas globales même si elles sont précédées d'un signe dollar.
Andrew Grimm
@AndrewGrimm Merci d'avoir signalé cela. Je n'en étais pas conscient. Je vais devoir changer beaucoup d'anciens codes: - /
Yossi
Vous pouvez aussi faire $1, $2... $9ou Regexp.last_match(1)comme recommandé par rubocop
Edgar Ortega
6

Voici une approche alternative qui vous donne le même résultat mais n'utilise pas de commutateur. Si vous mettez vos expressions régulières dans un tableau, vous pouvez faire quelque chose comme ceci:

res = [ /pat1/, /pat2/, ... ]
m   = nil
res.find { |re| m = foo.match(re) }
# Do what you will with `m` now.

Déclarer en mdehors du bloc lui permet d'être toujours disponible après avoir findterminé avec le bloc et finds'arrêtera dès que le bloc renvoie une valeur vraie afin que vous obteniez le même comportement de raccourci qu'un commutateur vous donne. Cela vous donne le plein MatchDatasi vous en avez besoin (peut-être que vous voulez utiliser des groupes de capture nommés dans vos expressions régulières) et sépare bien vos expressions régulières de votre logique de recherche (qui peut ou non produire un code plus clair), vous pouvez même charger vos expressions rationnelles à partir d'un config ou choisissez l'ensemble d'entre eux que vous vouliez au moment de l'exécution.

mu est trop court
la source
Je pensais aussi à la sécurité des threads en utilisant l' caseapproche. Peut-être souhaitez-vous utiliser l'approche de mu dans un scénario fileté, plutôt qu'une variable globale avec l'approche par cas (?)
Casper