Jak napisać instrukcję przełącznika Ruby (przypadek… kiedy) z wyrażeniem regularnym i odwołaniami wstecznymi?

89

Wiem, że mogę napisać instrukcję w języku Ruby, aby sprawdzić dopasowanie do wyrażeń regularnych. Chciałbym jednak użyć danych dopasowania w mojej instrukcji zwrotu. Coś takiego jak ten pół-pseudokod:

foo = "10/10/2011"

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

Jak mogę to osiągnąć?

Dzięki!


Uwaga: rozumiem, że nigdy nie użyłbym instrukcji przełącznika w prostym przypadku, jak powyżej, ale to tylko jeden przykład. W rzeczywistości to, co próbuję osiągnąć, to dopasowanie wielu potencjalnych wyrażeń regularnych do daty, którą można zapisać na różne sposoby, a następnie odpowiednio przeanalizuję ją za pomocą klasy Date Rubiego.

Yuval Karmi
źródło
1
Ruby's Date.parse rozumie wiele formatów dat. Próbowałeś tego?
raine
Chociaż nie odpowiada na to pytanie, możesz spojrzeć na chroniczny klejnot ...
DGM,

Odpowiedzi:

156

Odniesienia do najnowszych grup dopasowywania wyrażeń regularnych są zawsze przechowywane w pseudozmiennych, $1 aby $9:

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

Możesz także użyć $LAST_MATCH_INFOpseudozmiennej, aby uzyskać dostęp do całego MatchDataobiektu. Może to być przydatne w przypadku używania nazwanych przechwytywania:

case foo
when /^(?<number>[0-9][0-9])/
    print "the month is #{$LAST_MATCH_INFO['number']}"
else
    print "something else"
end
Yossi
źródło
1
@Yossi Czy masz źródło komentarza dotyczącego bezpieczeństwa wątków? Właśnie przeprowadziłem eksperyment w Rubim 1.8.7, który wydaje się wskazywać, że jest bezpieczny dla wątków! (Wątek pasujący do wyrażenia regularnego co sekundę - sprawdzanie w irb, czy lokalne dopasowania są obijane)
Joel
5
-1 $ zmienne związane z wyrażeniami regularnymi nie są globalne, mimo że przed nimi znajduje się znak dolara.
Andrew Grimm,
@AndrewGrimm Dzięki za wskazanie tego. Nie byłem tego świadomy. Będę musiał zmienić dużo starego kodu: - /
Yossi
Można również zrobić $1, $2... $9lub Regexp.last_match(1)jak zaleca rubocop
Edgar Ortega
6

Oto alternatywne podejście, które daje ten sam wynik, ale nie używa przełącznika. Jeśli umieścisz swoje wyrażenia regularne w tablicy, możesz zrobić coś takiego:

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

Deklarowanie mpoza blokiem pozwala na to, aby był on nadal dostępny po zakończeniu findz blokiem i findzatrzyma się, gdy tylko blok zwróci wartość true, dzięki czemu uzyskasz takie samo zachowanie skrótów, jakie daje przełącznik. Daje ci to pełne, MatchDatajeśli tego potrzebujesz (być może chcesz użyć nazwanych grup przechwytywania w swoich wyrażeniach regularnych) i ładnie oddziela twoje wyrażenia regularne od logiki wyszukiwania (co może, ale nie musi, dać jaśniejszy kod), możesz nawet załadować swoje wyrażenia regularne z config lub wybierz, który z nich chcesz w czasie wykonywania.

mu jest za krótkie
źródło
Myślałem również o bezpieczeństwie nici przy użyciu tego casepodejścia. Może chcesz użyć podejścia mu w scenariuszu wątkowym, zamiast zmiennej globalnej z podejściem do przypadku (?)
Casper