), где name — имя, начинающееся с буквы (как идентификаторы в Ruby). Обратите внимание на сходство этой конструкции с неименованным атомарным подвыражением.
Для чего может понадобиться именованное выражение? Например, для того, чтобы сослаться на него внутри обратной ссылки. Ниже приведен пример простого регулярного выражения для сопоставления с повторяющимся словом (см. также раздел 3.14.6):
re1 = /\s+(\w+)\s+\1\s+/
str = "Now is the the time for all..."
re1.match(str).to_a # ["the the","the"]
Здесь
мы запомнили слово, а затем сослались на него по номеру
\1
. Примерно так же можно пользоваться ссылками на именованные выражения. При первом обнаружении подвыражения ему присваивается имя, а в обратной ссылке употребляется символ
\k
, за которым следует это имя (всегда в угловых скобках):
re2 = /\s+(?<anyword>\w+)\s+\k<anyword>\s+/
Второй вариант длиннее, зато понятнее. (Имейте в виду, что в одном и том же регулярном выражении нельзя использовать и именованные, и нумерованные обратные ссылки.) Если нравится, пользуйтесь!
В Ruby уже давно можно включать обратные ссылки в строки, передаваемые методам
sub
и
gsub
. Раньше с этой целью допускалось лишь использование нумерованных ссылок, но в самых последних версиях именованные тоже разрешены:
str = "I breathe when I sleep"
# Нумерованные соответствия...
r1 = /I (\w+) when I (\w+)/
s1 = str.sub(r1,' I \2 when I \1')
# Именованные соответствия...
r1 = /I (?<verb1>\w+) when I (?<verb2>\w+)/
s2 = str.sub(r2,'I \k<verb2> when I \k<verb1>')
Puts s1 # I sleep when I breathe
Puts s2 # I sleep when I breathe
Еще одно возможное применение именованных выражений — повторное употребление выражения. В таком случае перед именем ставится символ
\g
(а не
\k
). Определим, например, образец spaces так, чтобы можно было использовать его многократно. Тогда последнее выражение примет вид:
Обратите внимание, что этот образец многократно употребляется с помощью маркера
\g
. Особенно удобна такая возможность в рекурсивных регулярных выражениях, но это тема следующего раздела.
Нотацией
\g<1>
можно пользоваться и тогда, когда именованных подвыражений нет. Тогда запомненное ранее подвыражение вызывается по номеру, а не по имени.
И последнее замечание об именованных соответствиях. В самых последних версиях Ruby имя (в виде строки или символа) может передаваться методу
MatchData
в качестве индекса, например:
str = "My hovercraft is full of eels"
reg = /My (?<noun>\w+) is (?<predicate>.*)/
m = reg.match(str)
puts m[:noun] # hovercraft
puts m["predicate"] # full of eels
puts m[1] #
то же, что m[:noun] или m["noun"]
Как видите, обычные индексы тоже не запрещены. Обсуждается возможность добавить в объект
MatchData
и синглетные методы.
puts m.noun
puts m.predicate
Но во время работы над книгой это еще не было реализовано.
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)"]
Отметим, что левосторонняя рекурсия запрещена. Следующий пример допустим:
Ошибка объясняется наличием рекурсивного обращения в начале каждой альтернативы. Немного подумав, вы поймете, что это приведет к бесконечному возврату.
3.14. Примеры регулярных выражений
В этом разделе мы приведем краткий перечень регулярных выражений, которые могут оказаться полезны на практике или просто послужат учебными примерами. Для простоты примеров ни одно выражение не зависит от наличия Oniguruma.