Регулярные выражения Perl и их применение

ba2f5a3f

Группировка элементов шаблона


Мы уже применяли захватывающие и просто группирующие скобки. Рассмотрим еще некоторые тонкости.

Как работают группирующие скобки с квантификаторами? Например, что захватят в переменную $1 скобки в регулярном выражении 'abc' =~ /(\w)*/ ? Это выражение похоже на (\w*), которое в переменную $1 захватит все три символа, но алгоритм его работы иной. Скобки в шаблоне (\w)* захватят последний символ - c. Для тех, кто впервые с этим сталкивается, это кажется непонятным. Это работает так: сначала \w совпадет с буквой a, затем квантификатор * будет заставлять повторяться всю внутрискобочную конструкцию снова и снова, насколько это возможно, оставляя при этом сохраненные состояния. При возвратах, когда происходит выход за открывающую захватывающую скобку, соответствующая ей нумерованная переменная становится неопределенной (перестает существовать). Если же при возврате происходит вход внутрь захватывающих скобок, то при следующем выходе за закрывающую скобку произойдет коррекция значения этой нумерованной переменной. Механизм работы с нумерованными переменными такой: при встрече открывающей скобки при движении вправо в структуре данных для соответствующей нумерованной переменной запоминается адрес позиции в результирующем тексте, где она начинается, а при выходе за закрывающую захватывающую скобку запоминается адрес позиции, где значение этой переменной заканчивается. Таким образом, нумерованные переменные не копируют фрагменты найденного текста, а просто ссылаются на них.

В нашем случае выражение \w повторится трижды, захватывая каждый раз следующую букву, и в конце концов захваченной окажется буква c. Мы получаем интересный тактический прием захвата последнего фрагмента текста, который соответствует заданному подшаблону. Я думаю, вы уже можете догадаться, как в общем случае можно захватить такой фрагмент текста, который стоит на n-ном месте с начала или конца ряда таких фрагментов. Например, чтобы захватить предпоследний символ, надо записать

/(\w)*\w/

А как бы стал работать этот шаблон, если бы квантификатор был минимальным? В этом случае

'abc' =~ /(\w)*?/;


скобки бы совпали с пустым фрагментом в начале строки, а переменная $ 1 получила бы неопределенное значение. Ее попросту не было бы ввиду нулевого значения квантификатора. Если бы этот квантификатор стоял внутри скобок:

'abc' =~ /(\w*?)/;

то переменная $1 существовала бы и имела бы пустое значение.

Теперь рассмотрим несохраняющие (группирующие) скобки:

(?: шаблон )

Шаблон в них может и отсутствовать (быть пустым).

Такие скобки, как мы знаем, не создают нумерованной переменной, а служат для группировки подшаблонов, чтобы поставить к этой группе квантификатор или отделить альтернативные шаблоны от остального регулярного выражения. Между знаком вопроса и двоеточием могут стоять квантификаторы, например:

(?im:^passport nr\. \d+)

Область действия этих квантификаторов ограничена шаблоном, стоящим в этих группирующих скобках. Это означает, что внутри этих скобок будет действовать режим поиска без учета регистра и метасимвол ^ будет совпадать не только в начале текста, но также в начале каждой логической строки, т.е. сразу после символа \n, если толко он не стоит самым последним в тексте. Но, конечно, эти режимы могут быть отменены другими квантификаторами, которые могут находиться в шаблонах внутри этих скобок.

Аналогично можно отменить действие модификаторов, если поставить перед ними знак минус. Например:

(?-i:^passport nr\. \d+)

Поиск будет вестись с учетом регистра символов. Или

(?i-ms:^passport nr\. \d+)

Включается модификатор i и отключаются модификаторы m и s.

И в конце добавлю, что скобки, как и другие метасимволы в регулярном выражении, нельзя задать как эскейп-последовательность: \x3a вместо (. Но! Забегая вперед, скажу, что почти произвольные части регулярного выражения могут находиться внутри интерполированных переменных. Смоторите, как это работает:

my $a='('; 'abc' =~ /$a\w)*/; print $1;

На печать выйдет символ c. Этот не описанный в руководствах способ работает потому, что интерполяция переменных в регулярном выражении происходит на ранней стадии обработки, что открывает большие возможности для комбинаций: ведь содержимое этих интерполируемых переменных можно менять от одного обращения к данному оператору поиска/замены - к другому. Например, в зависимости от предыдущих вычислений можно добавить пару захватывающих скобок или изменить квантификатор.


Содержание раздела