Come scrivere un'istruzione switch Ruby (case ... when) con regex e backreferences?


89

So che posso scrivere un'istruzione case Ruby per verificare una corrispondenza con espressioni regolari. Tuttavia, vorrei utilizzare i dati di corrispondenza nella mia dichiarazione di ritorno. Qualcosa di simile a questo semi-pseudocodice:

foo = "10/10/2011"

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

Come posso ottenerlo?

Grazie!


Solo una nota: capisco che non userei mai un'istruzione switch per un caso semplice come sopra, ma questo è solo un esempio. In realtà, quello che sto cercando di ottenere è l'abbinamento di molte potenziali espressioni regolari per una data che può essere scritta in vari modi, e quindi analizzarla di conseguenza con la classe Date di Ruby's.


1
Ruby's Date.parse comprende molti formati di data. L'hai provato?
raine

Anche se non risponde a questa domanda, potresti voler guardare la gemma cronica ...
DGM

Risposte:


156

I riferimenti agli ultimi gruppi di corrispondenza regex sono sempre memorizzati in pseudo variabili $1 per $9:

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

È inoltre possibile utilizzare la $LAST_MATCH_INFOpseudo variabile per ottenere l'intero MatchDataoggetto. Questo può essere utile quando si utilizzano acquisizioni con nome:

case foo
when /^(?<number>[0-9][0-9])/
    print "the month is #{$LAST_MATCH_INFO['number']}"
else
    print "something else"
end

1
@Yossi Hai una fonte per il tuo commento sulla sicurezza dei thread? Ho appena fatto un esperimento in Ruby 1.8.7 che sembra indicare che è thread-safe! (Thread che abbina una regex ogni secondo - controlla in irb se le corrispondenze locali vengono distrutte)
Joel

5
-1 Le variabili $ che hanno a che fare con le espressioni regolari non sono globali anche se hanno un segno di dollaro davanti.
Andrew Grimm

@AndrewGrimm Grazie per averlo sottolineato. Non ne ero consapevole. Dovrò cambiare molto vecchio codice: - /
Yossi

Puoi anche farlo $1, $2... $9o Regexp.last_match(1)come raccomandato da rubocop
Edgar Ortega

6

Ecco un approccio alternativo che ti dà lo stesso risultato ma non utilizza un interruttore. Se metti le tue espressioni regolari in un array, potresti fare qualcosa del genere:

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

Dichiarare mal di fuori del blocco consente che sia ancora disponibile dopo aver terminato findcon il blocco e findsi fermerà non appena il blocco restituirà un valore vero in modo da ottenere lo stesso comportamento di scorciatoia che ti dà un interruttore. Questo ti dà il pieno MatchDatase ne hai bisogno (forse vuoi usare gruppi di cattura con nome nelle tue espressioni regolari) e separa piacevolmente le tue espressioni regolari dalla logica di ricerca (che può o meno produrre un codice più chiaro), potresti persino caricare le tue espressioni regolari da un config o scegli quale set di essi desideri in fase di esecuzione.


Stavo anche pensando alla sicurezza dei thread utilizzando l' caseapproccio. Forse vuoi usare l'approccio di mu in uno scenario a thread, piuttosto che una variabile globale con l'approccio case (?)
Casper
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.