Wie schreibe ich eine Ruby-Switch-Anweisung (case… when) mit Regex und Backreferences?

86

Ich weiß, dass ich eine Ruby-case-Anweisung schreiben kann, um eine Übereinstimmung mit regulären Ausdrücken zu überprüfen. Ich möchte jedoch die Übereinstimmungsdaten in meiner return-Anweisung verwenden. So etwas wie dieser Halbpseudocode:

foo = "10/10/2011"

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

Wie kann ich das erreichen?

Vielen Dank!


Nur eine Anmerkung: Ich verstehe, dass ich für einen einfachen Fall wie oben niemals eine switch-Anweisung verwenden würde, aber das ist nur ein Beispiel. In Wirklichkeit versuche ich, viele potenzielle reguläre Ausdrücke für ein Datum abzugleichen, das auf verschiedene Arten geschrieben werden kann, und es dann entsprechend mit Rubys Datumsklasse zu analysieren.

Yuval Karmi
quelle
1
Ruby's Date.parse versteht viele Datumsformate. Hast du es versucht?
Raine
Obwohl es diese Frage nicht beantwortet, möchten Sie vielleicht einen Blick auf das chronische Juwel werfen ...
DGM

Antworten:

151

Die Verweise auf die neuesten Regex-Übereinstimmungsgruppen werden immer in Pseudovariablen gespeichert , $1 um $9:

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

Sie können auch die $LAST_MATCH_INFOPseudovariable verwenden, um auf das gesamte MatchDataObjekt zuzugreifen. Dies kann nützlich sein, wenn benannte Captures verwendet werden:

case foo
when /^(?<number>[0-9][0-9])/
    print "the month is #{$LAST_MATCH_INFO['number']}"
else
    print "something else"
end
Yossi
quelle
1
@Yossi Haben Sie eine Quelle für Ihren Kommentar zur Thread-Sicherheit? Ich habe gerade ein Experiment in Ruby 1.8.7 durchgeführt, das darauf hinweist, dass es threadsicher ist! (Thread, der jede Sekunde mit einem regulären Ausdruck übereinstimmt - prüft in irb, ob lokale Übereinstimmungen überfüllt sind)
Joel
5
-1 $ -Variablen, die mit regulären Ausdrücken zu tun haben, sind nicht global, obwohl ein Dollarzeichen davor steht.
Andrew Grimm
@ AndrewGrimm Danke, dass du darauf hingewiesen hast. Ich war mir dessen nicht bewusst. Ich muss viel alten Code ändern: - /
Yossi
Sie können auch tun $1, $2... $9oder Regexp.last_match(1)wie von Rubocop empfohlen
Edgar Ortega
6

Hier ist ein alternativer Ansatz, mit dem Sie das gleiche Ergebnis erzielen, jedoch keinen Schalter verwenden. Wenn Sie Ihre regulären Ausdrücke in ein Array einfügen, können Sie Folgendes tun:

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

Wenn Sie maußerhalb des Blocks deklarieren, ist dieser nach Abschluss des Blocks weiterhin verfügbar findund findwird angehalten, sobald der Block einen wahren Wert zurückgibt, sodass Sie das gleiche Verknüpfungsverhalten erhalten, das Sie von einem Schalter erhalten. Dies gibt Ihnen die volle Leistung, MatchDatawenn Sie es benötigen (vielleicht möchten Sie benannte Erfassungsgruppen in Ihren regulären Ausdrücken verwenden) und trennt Ihre regulären Ausdrücke gut von Ihrer Suchlogik (die möglicherweise klareren Code liefert oder nicht). Sie können sogar Ihre regulären Ausdrücke aus a laden Konfigurationsdatei oder wählen Sie, welche Gruppe von ihnen zur Laufzeit gewünscht wird.

mu ist zu kurz
quelle
Ich habe auch über die Thread-Sicherheit mit diesem caseAnsatz nachgedacht . Vielleicht möchten Sie den Ansatz von mu in einem Thread-Szenario verwenden, anstatt eine globale Variable mit dem Fall-Ansatz (?)
Casper