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

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

Жанры

Программирование на языке 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 включительно). Как и раньше, для удобства восприятия образец разбит на части:

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

Последний герой

Грэйдо Эллениэль и Эстель
2. Последний герой
Фантастика:
фэнтези
юмористическое фэнтези
6.60
рейтинг книги
Последний герой

Костры амбиций

Вулф Том
21. Иллюминатор
Проза:
современная проза
7.43
рейтинг книги
Костры амбиций

Дикарка Жасмин

Смолл Бертрис
6. Сага о Скай О`Малли
Любовные романы:
исторические любовные романы
8.90
рейтинг книги
Дикарка Жасмин

Над бездной

Делински Барбара
Любовные романы:
современные любовные романы
7.29
рейтинг книги
Над бездной

Бар «Безнадега»

Вольная Мира
3. Другая сторона: темные предания
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Бар «Безнадега»

Имперец. Том 3

Романов Михаил Яковлевич
2. Имперец
Фантастика:
боевая фантастика
попаданцы
альтернативная история
7.50
рейтинг книги
Имперец. Том 3

Старплекс. Конец эры (сборник)

Сойер Роберт Джеймс
Шедевры фантастики
Фантастика:
научная фантастика
6.25
рейтинг книги
Старплекс. Конец эры (сборник)

Эпоха Опустошителя. Том IX

Павлов Вел
9. Вечное Ристалище
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Эпоха Опустошителя. Том IX

Аврора Горелика (сборник)

Аксенов Василий Павлович
Поэзия:
драматургия
6.25
рейтинг книги
Аврора Горелика (сборник)

Лейб-хирург

Дроздов Анатолий Федорович
2. Зауряд-врач
Фантастика:
альтернативная история
7.34
рейтинг книги
Лейб-хирург

Сиротка

Дюпюи Мари-Бернадетт
1. Сиротка
Проза:
историческая проза
8.76
рейтинг книги
Сиротка

Королевский крест

Панов Вадим Юрьевич
10. Тайный Город
Фантастика:
боевая фантастика
9.09
рейтинг книги
Королевский крест

"Фантастика 2024-164". Компиляция. Книги 1-25

Злотников Роман Валерьевич
Фантастика 2024. Компиляция
Фантастика:
боевая фантастика
попаданцы
альтернативная история
аниме
фэнтези
5.00
рейтинг книги
Фантастика 2024-164. Компиляция. Книги 1-25

Я до сих пор царь. Книга XXXII

Дрейк Сириус
32. Дорогой барон!
Фантастика:
юмористическое фэнтези
аниме
попаданцы
5.00
рейтинг книги
Я до сих пор царь. Книга XXXII