Эти образцы могут оказаться полезны, если вы хотите убедиться, что строка содержит число, перед тем как пытаться преобразовать ее.
3.14.5 Сопоставление с датой и временем
Пусть надо выделить дату и время, записанные в формате mm/dd/yy hh:mm:ss
. Вот первая попытка: datetime = /(dd)/(dd)/(dd) (dd): (dd): (dd)/
.
Но такой образец распознает некоторые некорректные даты и отвергает правильные. Следующий вариант более избирателен. Обратите внимание, как мы строим его путем интерполяции мелких регулярных выражений в более крупное:
mo = /(0?[1-9]|1[0-2])/ # От 01 до 09 или от 1 до 9 или 10-12.
dd = /([0-2]?[1-9]| [1-3][01])/ # 1-9 или 01-09 или 11-19 и т.д.
yy = /(dd)/ # 00-99
hh = /([01]?[1-9]|[12][0-4])/ # 1-9 или 00-09 или...
mi = /([0-5]d)/ # 00-59, обе цифры должны присутствовать.
ss = /([0-6]d)?/ # разрешены еще и доли секунды ;-)
date = /(#{mo}/#{dd}/#{yy})/
time = /{#{hh}:#{mi}:#{ss})/
datetime = /(#{date} #{time})/
Вот как можно вызвать это регулярное выражение из метода String#scan
, чтобы получить массив соответствий:
str='Recorded on 11/18/07 20:31:00'
str.scan(datetime)
# [['11/18/07 20:31:00', '11/18/07', '11', '18', '00',
# '20:31:00', '20', '31', ':00']]
Разумеется, все это можно было сделать с помощью одного большого регулярного выражения:
datetime = %r{(
(0?[1-9]|1[0-2])/ # mo: от 01 до 09 или от 1 до 9 или 10-12.
([0-2]?[1-9]|[1-3][01])/ # dd: 1-9 или 01-09 или 11-19 и т. д.
(dd) [ ] # yy: 00-99
([01]?[1-9]|[12][0-4]): # hh: 1-9 или 00-09 или...
([0-5]d): # mm: 00-59, обе цифры должны присутствовать.
(([0-6]d))? # ss: разрешены еще и доли секунды ;-)
)}x
Обратите внимание на конструкцию %r{}
, позволяющую не экранировать символы обратной косой черты.
3.14.6. Обнаружение повторяющихся слов в тексте
В этом разделе мы реализуем детектор повторяющихся слов. Повторение одного и того же слова два раза подряд — типичная опечатка. Следующий код распознает такие ситуации:
double_re = /(['A-Z]+) +1/i
str='There's there's the the pattern.'
str.scan(double_re) # [['There's'],['the']]
Обратите внимание на модификатор i
в конце выражения, он позволяет проводить сопоставление без учета регистра. Каждой группе соответствует массив, поэтому в результате получается массив массивов.
3.14.7. Поиск слов, целиком набранных прописными буквами
Мы упростили пример, предположив, что в тексте нет чисел, подчерков и т.д.
allcaps = /[A-Z]+/
string = 'This is ALL CAPS'
string[allcaps] # 'ALL'
Suppose you want to extract every word in all-caps:
string.scan(allcaps) # ['ALL', 'CAPS']
При желании можно было бы обобщить эту идею на идентификаторы Ruby и аналогичные вещи.
3.14.8. Сопоставление с номером версии
Принято присваивать библиотекам и приложениям номера версий, состоящие из трёх чисел, разделенных точками. Следующее регулярное выражение сопоставляется с подобной строкой, выделяя попутно имя пакета и отдельные части номера версии:
package = 'mylib-1.8.12'
matches = package.match(/(.*)-(d+).(d+).(d+)/)
name, major, minor, tiny = matches[1..-1]
3.14.9. Еще несколько образцов
Завершим наш список несколькими выражениями из категории «разное». Как обычно, почти все эти задачи можно решить несколькими способами.
Пусть нужно распознать двузначный почтовый код американского штата. Проще всего, конечно, взять выражение /[A-Z]{2}/
. Но оно сопоставляется с такими строками, как XX
или ZZ
, которые допустимы, но бессмысленны. Следующий образец распознает все стандартные аббревиатуры, общим числом 51 (50 штатов и DC — округ Колумбия):
state = /^A[LKZR] ! C[AOT] | D[EC] | FL | GA | HI | I[DLNA] |
K[SY] | LA | M[EDAINSOT] | N[EVHJMYCD] | O[HKR] |
PA | RI | S[CD] | T[NX] | UT | V[TA] | W[AVIY]$/x
Для ясности я воспользовался обобщенным регулярным выражением (модификатор x
). Пробелы и символы новой строки в нем игнорируются.
Продолжая эту тему, приведем регулярное выражение для распознавания почтового индекса США (он может состоять из пяти или девяти цифр):
zip = /^d{5}(-d{4))?$/
Якоря (в этом и других выражениях) призваны лишь гарантировать, что ни до, ни после