Чтение онлайн

на главную - закладки

Жанры

Программирование на языке 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 "да"

else

 puts "нет"

end

3.14.2. Сопоставление с парой «ключ-значение»

Иногда приходится работать со строками вида «ключ=значение» (например, при разборе конфигурационного файла приложения).

Следующий код извлекает ключ и значение. Предполагается, что ключ состоит из одного слова, значение продолжается до конца строки, а знак равенства может быть окружен пробелами:

pat = /(\w+)\s*=\s*(.*?)$/

str = "color = blue"

matches = pat.match(str)

puts matches[1] # "color"

puts matches[2] # "blue"

3.14.3. Сопоставление с числами, записанными римскими цифрами

Следующее довольно сложное регулярное выражение сопоставляется с любым правильно записанным римскими цифрами числом (до 3999 включительно). Как и раньше, для удобства восприятия образец разбит на части:

Поделиться:
Популярные книги

Темные тропы и светлые дела

Владимиров Денис
3. Глэрд
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Темные тропы и светлые дела

Мастер 10

Чащин Валерий
10. Мастер
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Мастер 10

Мужчина моей судьбы

Ардова Алиса
2. Мужчина не моей мечты
Любовные романы:
любовно-фантастические романы
8.03
рейтинг книги
Мужчина моей судьбы

Идеальный мир для Лекаря 24

Сапфир Олег
24. Лекарь
Фантастика:
городское фэнтези
попаданцы
5.00
рейтинг книги
Идеальный мир для Лекаря 24

Чужак из ниоткуда

Евтушенко Алексей Анатольевич
1. Чужак из ниоткуда
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужак из ниоткуда

Кодекс Охотника

Винокуров Юрий
1. Кодекс Охотника
Фантастика:
фэнтези
юмористическое фэнтези
попаданцы
боевая фантастика
5.00
рейтинг книги
Кодекс Охотника

Господин из завтра. Тетралогия.

Махров Алексей
Фантастика:
альтернативная история
8.32
рейтинг книги
Господин из завтра. Тетралогия.

Герой

Мазин Александр Владимирович
4. Варяг
Фантастика:
альтернативная история
9.10
рейтинг книги
Герой

Точка Бифуркации VI

Смит Дейлор
6. ТБ
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Точка Бифуркации VI

Адепт. Том 1. Обучение

Бубела Олег Николаевич
6. Совсем не герой
Фантастика:
фэнтези
9.27
рейтинг книги
Адепт. Том 1. Обучение

Приказано выжить!

Малыгин Владимир
1. Другая Русь
Фантастика:
боевая фантастика
попаданцы
альтернативная история
7.09
рейтинг книги
Приказано выжить!

На границе империй. Том 10. Часть 3

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 3

Князь

Мазин Александр Владимирович
3. Варяг
Фантастика:
альтернативная история
9.15
рейтинг книги
Князь

Вернувшийся: Посол. Том IV

Vector
4. Вернувшийся
Фантастика:
космическая фантастика
киберпанк
5.00
рейтинг книги
Вернувшийся: Посол. Том IV