Программирование на языке Ruby
вернуться

Фултон Хэл

Шрифт:

3.13.7. Рекурсия в регулярных выражениях

Возможность повторно обращаться к подвыражению позволяет создавать рекурсивные регулярные выражения. Например, данный код находит любое вложенное выражение с правильно расставленными скобками (спасибо Эндрю Джексону):

str = "а * ((b-c)/(d-e) - f) * g"

reg = /(? # Начало именованного выражения.

\( # Открывающая круглая скобка.

(?: # Незапоминаемая группа.

(?> # Сопоставление с собственническим выражением:

\\[] # экранированная скобка

| # ЛИБО

[^] # вообще не скобка. )

) # Конец собственнического выражения.

| # ЛИБО

\g # Вложенная группа в скобках (рекурсивный вызов).

)* # Незапоминаемая группа повторяется нуль или

# более раз.

\) # Закрывающая круглая скобка.

) # Конец именованного выражения.

/x

m = reg.match(str).to_a # ["((b-c)/(d-e) - f)", "((b-c)/(d-e) - f)"]

Отметим, что левосторонняя рекурсия запрещена. Следующий пример допустим:

str = "bbbaccc"

re1 = /(?<foo>a|b\g<foo>c)/

re1.match(str).to_a # ["bbbaccc","bbbaccc"]

А такой — нет:

re2 = /(?<foo>a|\g<foo>c)/ # Синтаксическая ошибка!

Ошибка объясняется наличием рекурсивного обращения в начале каждой альтернативы. Немного подумав, вы поймете, что это приведет к бесконечному возврату.

3.14. Примеры регулярных выражений

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

3.14.1. Сопоставление с IP-адресом

Пусть мы хотим понять, содержит ли строка допустимый IPv4-адрес. Стандартно он записывается в точечно-десятичной нотации, то есть в виде четырех десятичных чисел, разделенных точками, причем каждое число должно находиться в диапазоне от 0 до 255.

Приведенный ниже образец решает эту задачу (за немногими исключениями типа «127.1»). Для удобства восприятия мы разобьем его на части. Отметим, что символ \d дважды экранирован, чтобы косая черта не передавалась из строки в регулярное выражение (чуть ниже мы решим и эту проблему).

num = "(\\d|[01]?\\d\\d|2[0-4]\\d\25[0-5])"

pat = ^(#{num}\.){3}#{num}$"

ip_pat = Regexp.new(pat)

ip1 = "9.53.97.102"

if ip1 =~ ip_pat # Печатается: "да"

 puts "да"

else

 puts "нет"

e
nd

Надо признать, что в определении переменной

num
слишком много символов обратной косой черты. Определим ее в виде регулярного выражения, а не строки:

num = /(\d1[01]?\d\d|2[0-4]\d|25[0-5])/

Когда одно регулярное выражение интерполируется в другое, вызывается метод

to_s
, который сохраняет всю информацию из исходного регулярного выражения.

num.to_s # "(?-mix:(\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5]))"

Иногда для встраивания удобно использовать регулярное выражение, а не строку. Хорошее эвристическое правило: интерполируйте регулярные выражения, если веских причин интерполировать строки.

IPv6-адреса пока не очень широко распространены, но для полноты рассмотрим и их. Они записываются в виде восьми шестнадцатеричных чисел, разделенных двоеточиями, с подавлением начальных нулей.

num = /[0-9A-Fa-f]{0,4}/

pat = /^(#{num}:){7}#{num}$/

ipv6_pat = Regexp.new(pat)

v6ip = "abcd::1324:ea54::dead::beef"

if v6ip =~ ipv6_pat # Печатается: "да"

 puts "да"

  • Читать дальше
  • 1
  • ...
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • ...

Private-Bookers - русскоязычная библиотека для чтения онлайн. Здесь удобно открывать книги с телефона и ПК, возвращаться к сохраненной странице и держать любимые произведения под рукой. Материалы добавляются пользователями; если считаете, что ваши права нарушены, воспользуйтесь формой обратной связи.

Моя полка

  • Моя полка

Связаться

  • help@private-bookers.win