Як написати оператор перемикача Ruby (випадок ... коли) із регулярними виразами та зворотними посиланнями?


86

Я знаю, що можу написати випадок із Ruby, щоб перевірити відповідність регулярних виразів. Однак я хотів би використати дані збігів у своєму звіті про повернення. Щось на зразок цього напівпсевдокоду:

foo = "10/10/2011"

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

Як я можу цього досягти?

Дякую!


Тільки примітка: я розумію, що ніколи не використовував би оператор switch для простого випадку, як зазначено вище, але це лише один приклад. Насправді, я намагаюся досягти відповідності багатьох потенційних регулярних виразів для дати, які можна записати різними способами, а потім аналізувати їх відповідно до класу Ruby's Date.


1
Ruby's Date.parse розуміє багато форматів дат. Ви пробували?
дощ

Хоча це питання не відповідає на це питання, можливо, ви захочете поглянути на перлину Хроніка ...
DGM

Відповіді:


153

Посилання на останні групи відповідних регулярних виразів завжди зберігаються у псевдо змінних $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 змінних $, що стосуються регулярних виразів, не є загальносвітовими, хоча перед ними є знак долара.
Andrew Grimm,

@AndrewGrimm Дякуємо, що вказали на це. Я цього не знав. Мені доведеться змінити багато старого коду: - /
Йоссі

Ви також можете зробити $1, $2... $9або Regexp.last_match(1)за рекомендацією rubocop
Едгар Ортега,

6

Ось альтернативний підхід, який дає той самий результат, але не використовує перемикач. Якщо ви помістите свої регулярні вирази в масив, ви можете зробити щось подібне:

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

Оголошення mпоза блоком дозволяє йому залишатися доступним після того, як findце буде зроблено з блоком, і findзупиниться, як тільки блок поверне справжнє значення, тож ви отримаєте ту саму поведінку ярликів, яку дає вам перемикач. Це дає вам повну інформацію, MatchDataякщо вам це потрібно (можливо, ви хочете використовувати іменовані групи захоплення у своїх регулярних виразах) і добре відокремлює ваші регулярні вирази від вашої логіки пошуку (яка може або не може дати чіткіший код), ви навіть можете завантажити свої регулярні вирази з конфігураційний файл або виберіть, який їх набір ви хотіли під час виконання.


Я також думав про безпеку потоків, використовуючи caseпідхід. Можливо, ви хочете використовувати підхід mu у потоковому сценарії, а не глобальну змінну з підходом case (?)
Каспер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.