Как написать оператор переключения Ruby (case… when) с регулярным выражением и обратными ссылками?

87

Я знаю, что могу написать оператор case на Ruby, чтобы проверять соответствие регулярным выражениям. Однако я хотел бы использовать данные совпадения в моем операторе возврата. Что-то вроде этого полу-псевдокода:

foo = "10/10/2011"

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

Как я могу этого добиться?

Благодарность!


Просто примечание: я понимаю, что я бы никогда не использовал оператор switch для простого случая, как указано выше, но это только один пример. На самом деле то, что я пытаюсь достичь, - это сопоставление многих потенциальных регулярных выражений для даты, которые могут быть записаны различными способами, и последующий их анализ с помощью класса Date Ruby.

Юваль Карми
источник
1
Ruby Date.parse понимает многие форматы даты. Ты это пробовал?
raine
Хотя он не дает ответа на этот вопрос, вы можете взглянуть на драгоценный камень хроники ...
DGM

Ответы:

155

Ссылки на последние согласования регулярных выражений групп всегда хранятся в псевдо переменных $1 , чтобы $9:

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

Вы также можете использовать $LAST_MATCH_INFOпсевдопеременную, чтобы получить доступ ко всему MatchDataобъекту. Это может быть полезно при использовании именованных захватов:

case foo
when /^(?<number>[0-9][0-9])/
    print "the month is #{$LAST_MATCH_INFO['number']}"
else
    print "something else"
end
Йосси
источник
1
@Yossi У вас есть источник вашего комментария относительно безопасности потоков? Я только что провел эксперимент с Ruby 1.8.7, который, кажется, показывает, что он поточно-ориентирован! (Нить, сопоставляющая регулярное выражение каждую секунду - проверка в irb, если локальные совпадения затираются)
Джоэл
5
-1 $ переменные, связанные с регулярными выражениями, не являются глобальными, даже если перед ними стоит знак доллара.
Эндрю Гримм
@AndrewGrimm Спасибо, что указали на это. Я не знал об этом. Придется много поменять старый код: - /
Йоси
Вы можете также сделать $1, $2... $9или Regexp.last_match(1)в соответствии с рекомендациями rubocop
Эдгар Ortega
6

Вот альтернативный подход, который дает тот же результат, но не использует переключатель. Если вы поместите свои регулярные выражения в массив, вы можете сделать что-то вроде этого:

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

Объявление mвне блока позволяет ему оставаться доступным после findзавершения работы с блоком и findостановится, как только блок вернет истинное значение, поэтому вы получите то же поведение быстрого доступа, которое дает вам переключатель. Это дает вам полную информацию, MatchDataесли вам это нужно (возможно, вы хотите использовать именованные группы захвата в своих регулярных выражениях) и хорошо отделяет ваши регулярные выражения от вашей логики поиска (что может дать или не дать более четкий код), вы даже можете загрузить свои регулярные выражения из config или выберите, какой из них вы хотите во время выполнения.

mu слишком короткий
источник
Я также думал о безопасности потоков, используя этот caseподход. Может быть, вы хотите использовать подход mu в многопоточном сценарии, а не глобальную переменную с подходом case (?)
Каспер