Spec-Zone.ru › Perl 5.36

perlre

СОДЕРЖАНИЕ

  • НАЗВАНИЕ
  • ОПИСАНИЕ
    • Основы
      • Метасимволы
    • Модификаторы
      • Обзор
      • Подробности о некоторых модификаторах
        • /x и /xx
        • Модификаторы набора символов
        • /l
        • /u
        • /d
        • /a (и /aa)
        • Какой модификатор набора символов активен?
        • Поведение модификаторов набора символов до Perl 5.14
    • Регулярные выражения
      • Квантификаторы
      • Последовательности экранирования
      • Классы символов и другие специальные экранирования
      • Утверждения
      • Группы захвата
    • Цитата метасимволов
    • Расширенные шаблоны
    • Обратная отслеживание
    • Запуск скрипта
    • Специальные глаголы управления обратной отслеживанием
    • Предупреждение об использовании \1 вместо $1
    • Повторяющиеся шаблоны, сопоставляющие подстроку нулевой длины
    • Комбинирование частей регулярных выражений
    • Создание собственных движков регулярных выражений
    • Частота выполнения встраиваемого кода
    • Поддержка PCRE/Python
  • ОШИБКИ
  • СМОТРИТЕ ТАКЖЕ

НАЗВАНИЕ

perlre - Регулярные выражения Perl

ОПИСАНИЕ

На этой странице описывается синтаксис регулярных выражений в Perl.

Если вы раньше не работали с регулярными выражениями, вводный учебник доступен в perlretut. Если вы немного знакомы с ними, краткое введение доступно в perlrequick.

За исключением раздела "Основы", на этой странице предполагается, что вы знакомы с основами регулярных выражений, такими как что такое «шаблон», как он выглядит и как он в основном используется. Для справки о том, как они используются, а также различных примеров того же, см. обсуждения m//, s///, qr// и "??" в "Операторы цитирования регулярных выражений" в perlop.

Новое в версии 5.22, use re 'strict' применяет более строгие правила, чем обычно, при компиляции шаблонов регулярных выражений. Он может найти то, что, хотя и законно, может не соответствовать вашим намерениям.

Основы

Регулярные выражения — это строки со специфическим синтаксисом и значением, описанными в этом документе и дополнительных документах, на которые он ссылается. Эти строки называются «шаблонами». Шаблоны используются для определения, содержит ли какая-либо другая строка, называемая «целевой», указанные в шаблоне характеристики. Мы называем это «сопоставлением» целевой строки с шаблоном. Обычно сопоставление выполняется путем использования целевой строки в качестве первого операнда, а шаблона — в качестве второго операнда, одного из двух бинарных операторов =~ и !~, перечисленных в "Операторы связи" в perlop; а шаблон был преобразован из обычной строки одним из операторов в "Операторы цитирования регулярных выражений" в perlop, как показано ниже:

$foo =~ m/abc/

Это возвращает значение «истина», если и только если строка в переменной $foo содержит последовательность символов «a», «b», а затем «c». (Оператор =~ m, или оператор сопоставления, описан в "m/PATTERN/msixpodualngc" в perlop.)

Шаблоны, которые еще не сохранены в какой-либо переменной, должны быть ограничены символами разделителей с обеих сторон. Они часто, как в примере выше, представляют собой прямые слэши, и типичный способ записи шаблона в документации — с помощью этих слэшей. В большинстве случаев разделитель является одним и тем же символом, перед и после, но есть несколько случаев, где символ выглядит как зеркальное отражение, где открытая версия является начальным разделителем, а закрытая — конечным разделителем, как

$foo =~ m<abc>

В большинстве случаев шаблон оценивается в контексте двойных кавычек, но можно выбрать разделители, чтобы принудительно использовать контекст одинарных кавычек, как

$foo =~ m'abc'

Если шаблон содержит свой разделитель внутри него, этот разделитель должен быть экранирован. Префикс с обратной косой чертой (например, "/foo\/bar/") служит этой цели.

Любой одиночный символ в шаблоне соответствует тому же символу в целевой строке, если этот символ не является метасимволом со специальным значением, описанным в этом документе. Последовательность неметасимволов соответствует той же последовательности в целевой строке, как мы видели выше с m/abc/.

Только несколько символов (все они являются знаками препинания ASCII) являются метасимволами. Наиболее часто используемый — это точка ".", которая обычно соответствует почти любому символу (включая саму точку).

Вы можете заставить символы, которые обычно действуют как метасимволы, интерпретироваться буквально, предваряя их обратной косой чертой, точно так же, как разделитель шаблона должен быть экранирован, если он также встречается в шаблоне. Таким образом, "\." соответствует только буквальной точке, "." вместо его обычного значения. Это означает, что обратная косая черта также является метасимволом, поэтому "\\" соответствует одному "\". А последовательность, содержащая экранированный метасимвол, соответствует той же последовательности (но без экранирования) в целевой строке. Таким образом, шаблон /blur\\fl/ соответствовал бы любой целевой строке, которая содержит последовательность "blur\fl".

Метасимвол "|" используется для сопоставления одного из двух вариантов. Таким образом

$foo =~ m/this|that/

истинно тогда и только тогда, когда $foo содержит либо последовательность "this", либо последовательность "that". Как и все метасимволы, предваряя "|" обратной косой чертой, вы делаете его соответствующим обычной знаку препинания; в его случае, вертикальной черте.

$foo =~ m/this\|that/

истинно тогда и только тогда, когда $foo содержит последовательность "this|that".

Вы не ограничены только одним "|".

$foo =~ m/fee|fie|foe|fum/

истинно тогда и только тогда, когда $foo содержит любую из этих 4 последовательностей из сказки для детей "Jack and the Beanstalk".

Как вы можете видеть, "|" связывает менее тесно, чем последовательность обычных символов. Мы можем переопределить это, используя метасимволы группирования, круглые скобки "(" и ")".

$foo =~ m/th(is|at) thing/

истинно тогда и только тогда, когда $foo содержит либо последовательность "this thing", либо последовательность "that thing". Части строки, которые соответствуют частям шаблона, заключенные в скобки, обычно делаются доступными отдельно для последующего использования в шаблоне, подстановке или программе. Это называется «захватом», и это может усложняться. См. "Группы захвата".

Первый вариант включает всё от последнего разделителя шаблона ("(", "(?:" (описано позже), и т.д. или начала шаблона) до первой "|", и последний вариант содержит всё от последней "|" до следующего закрывающего разделителя шаблона. Вот почему принято включать альтернативы в скобки: чтобы свести к минимуму путаницу о том, где они начинаются и заканчиваются.

Альтернативы проверяются слева направо, поэтому первый найденный вариант, для которого соответствует всё выражение, — это тот, который выбирается. Это означает, что альтернативы необязательно жадные. Например: при сопоставлении foo|foot с "barefoot", только часть "foo" будет соответствовать, так как это первая опробованная альтернатива, и она успешно соответствует целевой строке. (Это может показаться неважным, но это важно, когда вы захватываете сопоставленный текст, используя круглые скобки.)

Помимо удаления специального значения метасимвола, префикс обратной косой чертой изменяет некоторые буквы и цифры с сопоставления только самих себя на то, чтобы иметь специальное значение. Они называются «последовательности экранирования», и все они описаны в perlrebackslash. Последовательность обратной косой черты (буквы или цифры), которая в настоящее время не имеет специального значения для Perl, вызовет предупреждение, если включены предупреждения, поскольку они зарезервированы для потенциального будущего использования.

Одна такая последовательность — \b, которая соответствует границе какого-то рода. \b{wb} и некоторые другие дают специализированные типы границ. (Они все подробно описаны, начиная с "\b{}, \b, \B{}, \B" в perlrebackslash.) Обратите внимание, что они не соответствуют символам, а нулевым пространством между символами. Они являются примером утверждения нулевой ширины. Рассмотрим еще раз,

$foo =~ m/fee|fie|foe|fum/

Это возвращает значение «истина», если, помимо этих 4 слов, в $foo есть какие-либо из последовательностей «feed», «field», «Defoe», «fume» и многие другие. С помощью продуманного использования \b (или, лучше, (потому что оно разработано для обработки естественного языка) \b{wb}), мы можем убедиться, что сопоставлены только слова великана:

$foo =~ m/\b(fee|fie|foe|fum)\b/
$foo =~ m/\b{wb}(fee|fie|foe|fum)\b{wb}/

Последний пример показывает, что символы "{" и "}" являются метасимволами.

END_OF_DOCUMENT_MARKER

Другое применение последовательностей экранирования — указание символов, которые нельзя (или которые вы предпочитаете не) писать буквально. Подробное описание содержится в "Character Escapes" в perlrebackslash, но в следующих трёх абзацах кратко описаны некоторые из них.

Различные управляющие символы можно записать в стиле языка C: "\n" соответствует новой строке, "\t" — табуляции, "\r" — возврату каретки, "\f" — подаче формы, и т. д.

В более общем случае, \nnn, где nnn — строка из трёх восьмеричных цифр, соответствует символу, код которого — nnn. Вы легко можете столкнуться с проблемами, если у вас не ровно три цифры. Поэтому всегда используйте три, или, начиная с Perl 5.14, вы можете использовать \o{...} для указания любого числа восьмеричных цифр.

Аналогично, \xnn, где nn — шестнадцатеричные цифры, соответствует символу, порядковый номер которого — nn. Опять же, использование не ровно двух цифр — рецепт катастрофы, но вы можете использовать \x{...} для указания любого числа шестнадцатеричных цифр.

Помимо того, что "." является метасимволом, это пример «класса символов» — чего-то, что может соответствовать любому одиночному символу из заданного набора. В данном случае набор состоит из всех возможных символов. Perl предопределяет несколько классов символов помимо "."; есть отдельная справочная страница об этих классах, perlrecharclass.

Вы можете определять свои собственные пользовательские классы символов, помещая в ваше выражение в соответствующем месте(ах) список всех символов, которые вы хотите в наборе. Вы делаете это, заключая список в [] квадратные скобки. Эти классы называются «классами символов в квадратных скобках», если быть точным, но часто слово «в квадратных скобках» опускают. (Обычно это не приводит к путанице.) Это означает, что "[" символ — ещё один метасимвол. Он сам по себе ничего не сопоставляет; он используется только для того, чтобы указать Perl, что следующее за ним — класс символов в квадратных скобках. Если вы хотите сопоставить буквенный левый квадратный скобку, вы должны экранировать его, как "\[". Соответствующий "]" также является метасимволом; опять же, он сам по себе ничего не сопоставляет, а только отмечает конец вашего пользовательского класса для Perl. Это пример «метасимвола иногда». Это не метасимвол, если нет соответствующего "[", и сопоставляется со своим буквенным значением:

print "]" =~ /]/;  # prints 1

Список символов внутри класса символов определяет набор символов, соответствующих этому классу. "[abc]" сопоставляет одиночный символ "a" или "b" или "c". Но если первый символ после "[" — "^", то класс вместо этого сопоставляет любой символ, который не входит в список. Внутри списка "-" символ задаёт диапазон символов, так что a-z представляет все символы от "a" до "z" включительно. Если вы хотите, чтобы либо "-" или "]" сами были членом класса, поместите их в начало списка (возможно, после "^"), или экранируйте их обратным слэшем. "-" также воспринимается буквально, когда он находится в конце списка, непосредственно перед закрывающим "]". (Следующие все задают один и тот же класс из трёх символов: [-az], [az-], и [a\-z]. Все они отличаются от [a-z], которое задаёт класс, содержащий 26 символов, даже на наборах символов, основанных на EBCDIC.)

В классах символов в квадратных скобках есть много других аспектов; полные детали приведены в "Bracketed Character Classes" в perlrecharclass.

Метасимволы

"The Basics" ввёл некоторые из метасимволов. В этом разделе приведены все метасимволы. Большинство из них имеют тот же смысл, что и в команде egrep.

Только "\" всегда является метасимволом. Другие — метасимволы только иногда. В следующих таблицах перечислены все они, суммирована их роль и показаны контексты, в которых они являются метасимволами. Вне этих контекстов или если они опережаются "\", они сопоставляются с соответствующим значком пунктуации. В некоторых случаях их значение изменяется в зависимости от различных модификаторов шаблонов, изменяющих стандартное поведение. См. "Modifiers".

           PURPOSE                                  WHERE
\   Escape the next character                    Always, except when
                                                 escaped by another \
^   Match the beginning of the string            Not in []
      (or line, if /m is used)
^   Complement the [] class                      At the beginning of []
.   Match any single character except newline    Not in []
      (under /s, includes newline)
$   Match the end of the string                  Not in [], but can
      (or before newline at the end of the       mean interpolate a
      string; or before any newline if /m is     scalar
      used)
|   Alternation                                  Not in []
()  Grouping                                     Not in []
[   Start Bracketed Character class              Not in []
]   End Bracketed Character class                Only in [], and
                                                   not first
*   Matches the preceding element 0 or more      Not in []
      times
+   Matches the preceding element 1 or more      Not in []
      times
?   Matches the preceding element 0 or 1         Not in []
      times
{   Starts a sequence that gives number(s)       Not in []
      of times the preceding element can be
      matched
{   when following certain escape sequences
      starts a modifier to the meaning of the
      sequence
}   End sequence started by {
-   Indicates a range                            Only in [] interior
#   Beginning of comment, extends to line end    Only with /x modifier

Обратите внимание, что большинство метасимволов теряют своё специальное значение, когда они встречаются в классе символов в квадратных скобках, за исключением "^", которое имеет другое значение, когда находится в начале такого класса. И "-" и "]" являются метасимволами только в ограниченных позициях внутри классов символов в квадратных скобках; тогда как "}" является метасимволом только при закрытии специальной конструкции, начатой "{".

В контексте двойных кавычек, как это обычно бывает, нужно быть осторожным с "$" и не-метасимволом "@". Они могут интерполировать переменные, что может или не может быть тем, что вы имели в виду.

Эти правила были разработаны для компактности выражения, а не для удобочитаемости и поддерживаемости. Модификаторы шаблонов "/x и /xx" позволяют вставлять пробелы для улучшения удобочитаемости. Использование re 'strict' добавляет дополнительную проверку для обнаружения некоторых опечаток, которые могут беззвучно скомпилироваться в то, что вы не имели в виду.

По умолчанию символ "^" гарантированно сопоставляется только с началом строки, символ "$" — только с концом (или перед символом новой строки в конце), и Perl выполняет определённые оптимизации с предположением, что строка содержит только одну строку. Встроенные символы новой строки не будут сопоставляться с "^" или "$". Однако вы можете захотеть рассматривать строку как буфер с несколькими строками, так что "^" будет сопоставляться после любой новой строки в строке (кроме случая, когда новая строка — последний символ в строке), а "$" будет сопоставляться перед любой новой строкой. За счёт небольшого повышения накладных расходов это можно сделать, используя модификатор "/m" для оператора сопоставления с шаблоном. (Старые программы делали это, устанавливая $*, но этот параметр был удалён в perl 5.10.)

Для упрощения многострочных подстановок символ "." никогда не сопоставляется с новой строкой, если вы не используете модификатор /s, который, по сути, заставляет Perl рассматривать строку как одну строку — даже если это не так.

Модификаторы

Обзор

Стандартное поведение сопоставления можно изменить, используя различные модификаторы. Модификаторы, относящиеся к интерпретации шаблона, перечислены ниже. Модификаторы, изменяющие способ использования шаблона Perl, описаны в "Regexp Quote-Like Operators" в perlop и "Gory details of parsing quoted constructs" в perlop. Модификаторы можно добавлять динамически; см. "Extended Patterns" ниже.

m

Обрабатывать строку, с которой производится сопоставление, как многострочную. То есть, изменить "^" и "$" с сопоставления начала первой строки строки и конца последней строки на сопоставление начала и конца каждой строки в строке.

s

Обрабатывать строку как однострочную. То есть, изменить "." на сопоставление любого символа вообще, даже новой строки, что обычно оно не делает.

Используя вместе, как /ms, они позволяют "." сопоставлять любой символ вообще, всё ещё позволяя "^" и "$" сопоставляться, соответственно, только после и только перед новой строкой в строке.

i

Выполнить сопоставление с шаблоном без учёта регистра. Например, "A" будет соответствовать "a" при /i.

Если действуют правила сопоставления по локалям, то таблица соответствия регистров берется из текущей локали для кодовых точек меньше 255 и из правил Юникода для больших кодовых точек. Однако соответствия, которые пересекали бы границу правил Юникода/не-Юникода (ords 255/256), не будут успешны, если локаль не является UTF-8. См. perllocale.

Есть ряд символов Юникода, которые соответствуют последовательности нескольких символов при /i. Например, LATIN SMALL LIGATURE FI должен соответствовать последовательности fi. Perl в настоящее время не может сделать это, когда несколько символов находятся в шаблоне и разделены группами, или когда один или несколько из них квантифицированы. Таким образом

"\N{LATIN SMALL LIGATURE FI}" =~ /fi/i;          # Matches
"\N{LATIN SMALL LIGATURE FI}" =~ /[fi][fi]/i;    # Doesn't match!
"\N{LATIN SMALL LIGATURE FI}" =~ /fi*/i;         # Doesn't match!

# The below doesn't match, and it isn't clear what $1 and $2 would
# be even if it did!!
"\N{LATIN SMALL LIGATURE FI}" =~ /(f)(i)/i;      # Doesn't match!

Perl не сопоставляет несколько символов в классе символов в квадратных скобках, если не указан символ, который сопоставляется с ними, и он не сопоставляет их вообще, если класс символов инвертирован, что в противном случае могло бы быть очень запутанным. См. "Bracketed Character Classes" в perlrecharclass, и "Negation" в perlrecharclass.

x и xx

Улучшить удобочитаемость шаблона, разрешая пробелы и комментарии. Подробности в "/x и /xx"

p

Сохранить сопоставленную строку, чтобы ${^PREMATCH}, ${^MATCH}, и ${^POSTMATCH} были доступны для использования после сопоставления.

В Perl 5.20 и выше это игнорируется. Из-за нового механизма копирования при записи ${^PREMATCH}, ${^MATCH}, и ${^POSTMATCH} будут доступны после сопоставления независимо от модификатора.

a, d, l, и u

Эти модификаторы, все новые в 5.14, влияют на то, какие правила наборов символов (Unicode и т.д.) используются, как описано ниже в "Character set modifiers".

n

Предотвратить захват метасимволов группирования (). Этот модификатор, новый в 5.22, предотвратит заполнение $1, $2, и т.д. ...

"hello" =~ /(hi|hello)/;   # $1 is "hello"
"hello" =~ /(hi|hello)/n;  # $1 is undef

Это эквивалентно добавлению ?: в начало каждой захватывающей группы:

"hello" =~ /(?:hi|hello)/; # $1 is undef

/n может быть отменён на групповой основе. В качестве альтернативы можно использовать именованные захваты.

"hello" =~ /(?-n:(hi|hello))/n;   # $1 is "hello"
"hello" =~ /(?<greet>hi|hello)/n; # $1 is "hello", $+{greet} is
                                  # "hello"
Другие модификаторы

Есть ряд флагов, которые можно найти в конце конструкций регулярных выражений, которые не являются общими флагами регулярных выражений, но применяются к выполняемой операции, например, к сопоставлению или замене (m// или s/// соответственно).

Флаги, описанные далее в "Using regular expressions in Perl" в perlretut:

c  - keep the current position during repeated matching
g  - globally match the pattern repeatedly in the string

Модификаторы, специфичные для подстановки, описаны в "s/PATTERN/REPLACEMENT/msixpodualngcer" в perlop:

e  - evaluate the right-hand side as an expression
ee - evaluate the right side as a string then eval the result
o  - pretend to optimize your code, but actually introduce bugs
r  - perform non-destructive substitution and return the new value

Модификаторы регулярных выражений обычно записываются в документации как например, "модификатор /x", даже если разделитель, о котором идет речь, на самом деле не является косой чертой. Модификаторы /imnsxadlup также могут быть встроены в само регулярное выражение с помощью конструкции (?...), см. "Расширенные паттерны" ниже.

Подробности о некоторых модификаторах

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

/x и /xx

Один /x сообщает парсеру регулярных выражений игнорировать большинство пробелов, которые не являются обрамлёнными обратным слэшем или не находятся внутри скобочной группы символов. Вы можете использовать это, чтобы разбить ваше регулярное выражение на более читаемые части. Кроме того, символ "#" обрабатывается как метасимвол, вводящий комментарий, который охватывает до закрывающего разделителя шаблона или до конца текущей строки, если шаблон продолжается на следующей строке. Таким образом, это очень похоже на обычный Perl-комментарий. (Вы можете включить закрывающий разделитель в комментарий только если вы предваряете его обратным слэшем, поэтому будьте осторожны!)

Использование /x означает, что если вы хотите реальные пробелы или "#" символы в шаблоне (вне скобочной группы символов, которая не затронута /x), то вам нужно либо экранировать их (с помощью обратных слэшей или \Q...\E ) или закодировать их с помощью восьмеричного, шестнадцатеричного или \N{} или \p{name=...} экранирования. Бесполезно пытаться продолжить комментарий на следующей строке, экранируя \n обратным слэшем или \Q.

Вы можете использовать "(?#text)", чтобы создать комментарий, который заканчивается раньше, чем конец текущей строки, но text также не может содержать закрывающий разделитель, если он не экранирован обратным слэшем.

Распространённая ошибка - забывать, что символ "#" (вне скобочной группы символов) начинает комментарий под /x и не соответствует буквально. Просто имейте это в виду, когда пытаетесь понять, почему конкретный /x шаблон не работает как ожидалось. Внутри скобочной группы символов, "#" сохраняет своё неспециальное, буквальное значение.

Начиная с Perl v5.26, если модификатор имеет второй "x" внутри него, эффект одиночного /x увеличивается. Единственное отличие заключается в том, что внутри скобочных групп символов неэкранированные (обратным слэшем) пробелы и символы табуляции не добавляются в группу, и поэтому их можно вставлять, чтобы сделать группы более читаемыми:

/ [d-e g-i 3-7]/xx
/[ ! @ " # $ % ^ & * () = ? <> ' ]/xx

понять легче, чем сжатые эквиваленты

/[d-eg-i3-7]/
/[!@"#$%^&*()=?<>']/

Обратите внимание, что это, к сожалению, не означает, что ваши скобочные группы могут содержать комментарии или занимать несколько строк. # внутри группы символов всё ещё является просто буквальным #, и не вводит комментарий. И, если закрывающая скобка не находится в той же строке, что и открывающая, символ новой строки (и всё на следующих строках до тех пор, пока не будет завершён ] ) будет частью группы, так же, как если бы вы написали \n.

Взятые вместе, эти возможности значительно повышают читаемость регулярных выражений Perl. Вот пример:

# Delete (most) C comments.
$program =~ s {
    /\*     # Match the opening delimiter.
    .*?     # Match a minimal number of characters.
    \*/     # Match the closing delimiter.
} []gsx;

Обратите внимание, что всё внутри \Q...\E остаётся не затронутым /x. И обратите внимание, что /x не влияет на интерпретацию пробелов внутри единого многосимвольного конструкта. Например (?:...) не может иметь пробел между "(", "?", и ":". Внутри любых разделителей такого конструкта, разрешённые пробелы не затрагиваются /x, и зависят от конструкта. Например, все конструкции, использующие фигурные скобки в качестве разделителей, такие как \x{...}, могут иметь пробелы внутри, но рядом с фигурными скобками, но не где-либо ещё, и без не-пробельных символов. Исключение составляют свойства Unicode, которые следуют правилам Unicode, для которых см. "Свойства, доступные через \p{} и \P{}" в perluniprops.

Множество символов, которые считаются пробелами, это те, что Unicode называет "Пробелами шаблона", а именно:

U+0009 CHARACTER TABULATION
U+000A LINE FEED
U+000B LINE TABULATION
U+000C FORM FEED
U+000D CARRIAGE RETURN
U+0020 SPACE
U+0085 NEXT LINE
U+200E LEFT-TO-RIGHT MARK
U+200F RIGHT-TO-LEFT MARK
U+2028 LINE SEPARATOR
U+2029 PARAGRAPH SEPARATOR
Модификаторы набора символов

/d, /u, /a, и /l, доступные начиная с 5.14, называются модификаторами набора символов; они влияют на правила набора символов, используемые для регулярного выражения.

Модификаторы /d, /u, и /l вряд ли будут вам полезны, и поэтому вам не нужно слишком сильно о них беспокоиться. Они существуют для внутреннего использования Perl, так что сложные структуры данных регулярных выражений могут быть автоматически сериализованы и позже точно восстановлены, включая все их нюансы. Но, так как Perl не может хранить секреты, и могут быть редкие случаи, когда они полезны, они документированы здесь.

Модификатор /a может оказаться полезным. Его цель заключается в том, чтобы код, который работает в основном с данными ASCII, не должен был беспокоиться о Unicode.

Кратко, /l устанавливает набор символов в соответствии с текущим локалем, активным во время выполнения совпадения шаблона.

/u устанавливает набор символов в Unicode.

/a также устанавливает набор символов в Unicode, НО добавляет несколько ограничений для безопасного по отношению к ASCII сопоставления.

/d - это старое, проблемное поведение набора символов по умолчанию до 5.14. Его единственная цель - заставить работать старое поведение.

В любой момент времени действует ровно один из этих модификаторов. Их существование позволяет Perl сохранять первоначальное скомпилированное поведение регулярного выражения, независимо от правил, действующих при его фактическом выполнении. И если оно интерполировано в более крупное регулярное выражение, правила оригинального поведения по-прежнему применяются к нему и не влияют на другие части.

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

s/foo/\Ubar/il

будет сопоставлять "foo" с использованием правил текущего локали для регистронезависимого сопоставления, но /l не влияет на то, как работает \U. Скорее всего, вы хотите, чтобы оба они использовали правила локали. Для этого скомпилируйте регулярное выражение в рамках действия use locale. Это подразумевает добавление /l, и применяет правила локали к \U. Урок заключается в use locale, а не /l явно.

Аналогично, было бы лучше использовать use feature 'unicode_strings' вместо,

s/foo/\Lbar/iu

чтобы получить правила Unicode, так как \L в первом случае (но не обязательно во втором) также будет использовать правила Unicode.

Более подробная информация о каждом из модификаторов приведена ниже. Скорее всего, вам не нужно знать эти детали для /l, /u, и /d, и вы можете перейти к /a.

/l

означает использование правил текущего локали (см. perllocale) при сопоставлении шаблонов. Например, \w будет сопоставлять символы «слова» этого локали, а "/i" регистронезависимое сопоставление будет сопоставлять в соответствии с правилами преобразования регистра локали. Локаль, которая используется, будет той, которая активна в момент выполнения сопоставления шаблона. Это может не совпадать с локалью во время компиляции, и может отличаться от одного соответствия к другому, если имеется промежуточный вызов функции setlocale().

До версии v5.20 Perl не поддерживал многобайтовые локали. Начиная с той версии, поддерживаются UTF-8 локали. Другие многобайтовые локали вряд ли будут поддерживаться. Однако во всех локалях могут быть символы с кодами выше 255, и они всегда будут обрабатываться как Unicode независимо от используемой локали.

В соответствии с правилами Unicode есть несколько регистронезависимых соответствий, которые пересекают границу 255/256. За исключением UTF-8 локалей в Perl версии 5.20 и выше, это запрещено под /l. Например, 0xFF (на платформах ASCII) не регистронезависимо соответствует символу с кодом 0x178, LATIN CAPITAL LETTER Y WITH DIAERESIS, потому что 0xFF может не являться LATIN SMALL LETTER Y WITH DIAERESIS в текущей локали, и Perl не может знать, существует ли этот символ в локали, и если да, то какой у него код.

В UTF-8 локали в версиях 5.20 и выше единственное видимое различие между локалью и без локали в регулярных выражениях должно быть в загрязнении, если ваш Perl поддерживает проверку загрязнений (см. perlsec).

Этот модификатор может быть установлен по умолчанию с помощью use locale, но см. "Какой модификатор набора символов действует?".

/u

означает использование правил Unicode при сопоставлении шаблонов. На платформах ASCII это означает, что символы с кодами с 128 по 255 принимают свои значения Latin-1 (ISO-8859-1) (которые такие же, как и в Unicode). (В противном случае Perl считает их значения неопределёнными.) Таким образом, при этом модификаторе платформа ASCII фактически становится платформой Unicode; и поэтому, например, \w будет соответствовать любому из более чем 100_000 символов «слова» в Unicode.

В отличие от большинства локалей, которые специфичны для пары язык-страна, Unicode классифицирует все символы, которые являются буквами где-либо в мире, как \w. Например, ваша локаль может не считать LATIN SMALL LETTER ETH буквой (если вы не говорите по-ислендски), но Unicode считает. Аналогично, все символы, которые являются десятичными цифрами где-либо в мире, будут соответствовать \d; это сотни, а не 10, возможных совпадений. И некоторые из этих цифр выглядят как некоторые из 10 ASCII цифр, но обозначают разное число, поэтому человек может легко думать, что число имеет другое значение, чем на самом деле. Например, BENGALI DIGIT FOUR (U+09EA) очень похож на ASCII DIGIT EIGHT (U+0038), а LEPCHA DIGIT SIX (U+1C46) очень похож на ASCII DIGIT FIVE (U+0035). И \d+, может сопоставлять строки цифр, которые являются смесью из разных систем письма, создавая проблему безопасности. Например, мошеннический сайт может отображать цену чего-либо с помощью U+1C46, и для пользователя это будет выглядеть так, что что-то стоит 500 единиц, но на самом деле стоит 600. Браузер, который принудительно выполнял скрипты ("Скрипт выполнения"), предотвратил бы такое мошенническое отображение. "num()" в Unicode::UCD также можно использовать, чтобы разобраться с этим. Или модификатор /a можно использовать, чтобы принудительно заставить \d соответствовать только ASCII цифрам с 0 по 9.

Также, при использовании этого модификатора, регистронезависимое соответствие работает со всем набором символов Юникода. Например, KELVIN SIGN, соответствует буквам "k" и "K"; а LATIN SMALL LIGATURE FF соответствует последовательности "ff", что, если вы не подготовлены, может выглядеть как шестнадцатеричная константа, представляя ещё одну потенциальную проблему безопасности. Подробное обсуждение проблем безопасности Юникода см. в https://unicode.org/reports/tr36.

Этот модификатор может быть задан по умолчанию с помощью use feature 'unicode_strings, use locale ':not_characters', или use v5.12 (или выше), но см. "Какой модификатор набора символов активен?".

/d

ВАЖНО: Из-за непредсказуемого поведения этого модификатора, используйте его только для сохранения странной обратной совместимости. Используйте функцию unicode_strings в новом коде, чтобы избежать случайного включения этого модификатора по умолчанию.

Что делает этот модификатор? Он "Зависит"!

Этот модификатор означает использование правил соответствия, нативных для платформы, за исключением случаев, когда необходимо использовать правила Юникода, как показано ниже:

  1. Флаг UTF8 целевой строки флаг UTF8 (см. ниже) установлен; или

  2. Флаг UTF8 шаблона флаг UTF8 (см. ниже) установлен; или

  3. Шаблон явно упоминает код символа, превышающий 255 (скажем, с помощью \x{100}); или

  4. Шаблон использует имя Юникода (\N{...}); или

  5. Шаблон использует свойство Юникода (\p{...} или \P{...}); или

  6. Шаблон использует разрыв Юникода (\b{...} или \B{...}); или

  7. Шаблон использует "(?[ ])"

  8. Шаблон использует (*script_run: ...)

Что касается ссылок на "флаг UTF8" выше: обычно приложения Perl не должны думать об этом флаге. Он является частью внутренней части Perl, поэтому может меняться, когда Perl пожелает. /d может привести к непредсказуемым результатам. См. "Ошибку Юникода" в perlunicode. Эта ошибка стала довольно известной, что привело к появлению других (без ругательств) названий для этого модификатора, таких как "Dicey" и "Dodgy".

Ниже приведены примеры того, как это работает на платформе ASCII:

$str =  "\xDF";        #
utf8::downgrade($str); # $str is not UTF8-flagged.
$str =~ /^\w/;         # No match, since no UTF8 flag.

$str .= "\x{0e0b}";    # Now $str is UTF8-flagged.
$str =~ /^\w/;         # Match! $str is now UTF8-flagged.
chop $str;
$str =~ /^\w/;         # Still a match! $str retains its UTF8 flag.

В стандартной конфигурации Perl этот модификатор автоматически выбирается по умолчанию, когда ни один из других не выбран, поэтому ещё одно название для него (к сожалению) - "По умолчанию".

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

/a (и /aa)

Этот модификатор означает ограничение ASCII (или безопасность ASCII). Этот модификатор может использоваться дважды для усиления его эффекта.

При использовании один раз он заставляет последовательности \d, \s, \w, а также классы символов Posix соответствовать только в диапазоне ASCII. Таким образом, они возвращаются к своим значениям до версии 5.6, до Юникода. В версии /a, \d всегда означает точно цифры "0" по "9"; \s означает пять символов [ \f\n\r\t], и начиная с Perl v5.18, вертикальную табуляцию; \w означает 63 символа [A-Za-z0-9_]; и аналогично, все классы Posix, такие как [[:print:]] соответствуют только соответствующим символам диапазона ASCII.

Этот модификатор полезен для людей, которые случайно используют Юникод и не хотят быть обременёнными его сложностями и проблемами безопасности.

С помощью /a, можно написать \d с уверенностью, что он будет соответствовать только символам ASCII, и если возникнет необходимость соответствовать символам за пределами ASCII, можно вместо этого использовать \p{Digit} (или \p{Word} для \w). Существуют аналогичные \p{...} конструкции, которые могут соответствовать символам за пределами ASCII как для пробелов (см. "Пробелы" в perlrecharclass), так и для классов Posix (см. "Классы символов Posix" в perlrecharclass). Таким образом, этот модификатор не означает, что вы не можете использовать Юникод, он означает, что для получения соответствия Юникоду необходимо явно использовать конструкцию (\p{}, \P{}) которая сигнализирует о Юникоде.

Как можно ожидать, этот модификатор заставляет, например, \D означать то же, что и [^0-9]; на самом деле, все символы, не являющиеся ASCII, соответствуют \D, \S и \W. \b по-прежнему означает соответствие на границе между \w и \W, используя определения /a (аналогично для \B).

В противном случае, /a ведет себя как модификатор /u, в этом случае регистронезависимое соответствие использует правила Юникода; например, "k" будет соответствовать Юникоду \N{KELVIN SIGN} при соответствии /i, а символы в диапазоне Latin1, превышающие ASCII, будут использовать правила Юникода при регистронезависимом соответствии.

Чтобы запретить соответствия ASCII/не-ASCII (например, "k" с \N{KELVIN SIGN}), укажите "a" дважды, например /aai или /aia. (Первое вхождение "a" ограничивает \d, и т.д., а второе вхождение добавляет ограничения /i.) Но обратите внимание, что символы, находящиеся за пределами диапазона ASCII, будут использовать правила Юникода для соответствия /i, поэтому модификатор фактически не ограничивает соответствие только ASCII; он просто запрещает смешивание ASCII и не-ASCII.

Подводя итог, этот модификатор обеспечивает защиту для приложений, которые не хотят быть подвержены воздействию всего Юникода. Указание его дважды обеспечивает дополнительную защиту.

Этот модификатор может быть задан по умолчанию с помощью use re '/a' или use re '/aa'. Если вы это сделаете, у вас может возникнуть необходимость явно использовать модификатор /u, если есть несколько выражений регулярных выражений, где вы хотите использовать полные правила Юникода (но даже здесь лучше, если всё находилось бы под функцией "unicode_strings", вместе с use re '/aa'). Также см. "Какой модификатор набора символов активен?".

Какой модификатор набора символов активен?

Какой из этих модификаторов активен в данный момент в выражении регулярного выражения зависит от довольно сложной системы взаимодействий. Они разработаны таким образом, что вам обычно не нужно беспокоиться об этом, но этот раздел предоставляет подробности. Как объяснено ниже в "Расширенные шаблоны", можно явно указать модификаторы, которые применяются только к частям выражения регулярного выражения. Внутренний всегда имеет приоритет над внешними, а применяемый к всему выражению имеет приоритет над любыми значениями по умолчанию, которые описаны в остальной части этого раздела.

Pragma use re '/foo' может использоваться для установки модификаторов по умолчанию (включая эти) для регулярных выражений, скомпилированных в пределах его области видимости. Этот pragma имеет приоритет над другими pragmas, которые также изменяют значения по умолчанию.

В противном случае, use locale устанавливает модификатор по умолчанию в /l; а use feature 'unicode_strings, или use v5.12 (или выше) устанавливают значение по умолчанию в /u при отсутствии в одной области видимости как use locale, так и use bytes. (use locale ':not_characters' также устанавливает значение по умолчанию в /u, переопределяя любые простые use locale.) В отличие от упомянутых выше механизмов, эти механизмы влияют на операции, помимо сопоставления шаблонов регулярных выражений, и поэтому обеспечивают более последовательные результаты с другими операторами, включая использование \U, \l, и т.д. в заменах подстановки.

Если ни одно из вышеперечисленного не применимо, по причинам обратной совместимости, модификатор /d действует по умолчанию. Поскольку это может привести к непредсказуемым результатам, лучше указать, какой набор правил следует использовать.

Поведение модификатора набора символов до Perl 5.14

До версии 5.14 явных модификаторов не было, но /l подразумевался для regexes, скомпилированных в рамках области видимости use locale, а /d - в противном случае. Однако интерполяция regex в более крупный regex игнорировала исходную компиляцию в пользу того, что действовало в момент второй компиляции. Было несколько несоответствий (ошибок) с модификатором /d, где правила Юникода применялись неуместно и наоборот. \p{} не подразумевал правил Юникода, и ни одно вхождение \N{}, до версии 5.12.

Регулярные выражения

Квантификаторы

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

*           Match 0 or more times
+           Match 1 or more times
?           Match 1 or 0 times
{n}         Match exactly n times
{n,}        Match at least n times
{,n}        Match at most n times
{n,m}       Match at least n but not more than m times

(Если неэкранированная фигурная скобка встречается в контексте, отличном от одного из перечисленных выше квантификаторов, где она не является частью обратной последовательности, такой как \x{...}, она либо является фатальной синтаксической ошибкой, либо обрабатывается как обычный символ, обычно с предупреждением об устаревании. Чтобы избежать этого, вы можете предварять его обратной косой чертой ("\{") или заключить его в квадратные скобки ("[{]"). Это изменение позволит в будущем добавить синтаксические расширения (например, сделать нижнюю границу квантификатора необязательной) и улучшить проверку синтаксических ошибок квантификаторов).

Квантификатор "*" эквивалентен {0,}, квантификатор "+" эквивалентен {1,}, а квантификатор "?" эквивалентен {0,1}. n и m ограничены неотрицательными целочисленными значениями, меньшими заданного предела, определённого при построении Perl. Обычно это 65534 на самых распространённых платформах. Фактический предел можно увидеть в сообщении об ошибке, созданном кодом, таким как этот:

$_ **= $_ , / {$_} / for 2 .. 42;

По умолчанию квантифицированный подшаблон является "жадным", то есть он будет соответствовать как можно большему количеству раз (с учётом определённого начального положения), при этом позволяя остальной части шаблона соответствовать. Если вы хотите, чтобы он соответствовал минимальному возможному количеству раз, добавьте после квантификатора "?". Обратите внимание, что значения не изменяются, только "жадность":

*?        Match 0 or more times, not greedily
+?        Match 1 or more times, not greedily
??        Match 0 or 1 time, not greedily
{n}?      Match exactly n times, not greedily (redundant)
{n,}?     Match at least n times, not greedily
{,n}?     Match at most n times, not greedily
{n,m}?    Match at least n but not more than m times, not greedily

Обычно, когда квантифицированный подшаблон не позволяет остальной части общего шаблона соответствовать, Perl будет выполнять обратную подстановку. Однако это поведение иногда нежелательно. Поэтому Perl предоставляет и "поглощающий" формат квантификатора.

*+     Match 0 or more times and give nothing back
++     Match 1 or more times and give nothing back
?+     Match 0 or 1 time and give nothing back
{n}+   Match exactly n times and give nothing back (redundant)
{n,}+  Match at least n times and give nothing back
{,n}+  Match at most n times and give nothing back
{n,m}+ Match at least n but not more than m times and give nothing back

Например,

'aaaa' =~ /a++a/

никогда не будет соответствовать, так как a++ поглотит все "a" в строке и не оставит их для оставшейся части шаблона. Эта функция может быть чрезвычайно полезной, чтобы дать Perl подсказки о том, где он не должен выполнять обратную подстановку. Например, типичная проблема "сопоставить строку в двойных кавычках" может быть наиболее эффективно выполнена, если записать её как:

/"(?:[^"\\]++|\\.)*+"/

как известно, если заключительная кавычка не совпадает, обратное отслеживание не поможет. Более подробную информацию см. в независимом подвыражении "(?>pattern)"; притяжательные квантификаторы — всего лишь синтаксический сахар для этого конструкта. Например, приведенный выше пример также можно записать следующим образом:

/"(?>(?:(?>[^"\\]+)|\\.)*)"/

Обратите внимание, что модификатор притяжательного квантификатора нельзя комбинировать с модификатором нежадного соответствия. Это бессмысленно. Рассмотрим следующую таблицу эквивалентности:

Illegal         Legal
------------    ------
X??+            X{0}
X+?+            X{1}
X{min,max}?+    X{min}

Последовательности escape

Поскольку шаблоны обрабатываются как строки в двойных кавычках, следующее также работает:

\t          tab                   (HT, TAB)
\n          newline               (LF, NL)
\r          return                (CR)
\f          form feed             (FF)
\a          alarm (bell)          (BEL)
\e          escape (think troff)  (ESC)
\cK         control char          (example: VT)
\x{}, \x00  character whose ordinal is the given hexadecimal number
\N{name}    named Unicode character or character sequence
\N{U+263D}  Unicode character     (example: FIRST QUARTER MOON)
\o{}, \000  character whose ordinal is the given octal number
\l          lowercase next char (think vi)
\u          uppercase next char (think vi)
\L          lowercase until \E (think vi)
\U          uppercase until \E (think vi)
\Q          quote (disable) pattern metacharacters until \E
\E          end either case modification or quoted section, think vi

Подробности см. в "Quote and Quote-like Operators" в perlop.

Классы символов и другие специальные escape

Кроме того, Perl определяет следующее:

Sequence   Note    Description
 [...]     [1]  Match a character according to the rules of the
                  bracketed character class defined by the "...".
                  Example: [a-z] matches "a" or "b" or "c" ... or "z"
 [[:...:]] [2]  Match a character according to the rules of the POSIX
                  character class "..." within the outer bracketed
                  character class.  Example: [[:upper:]] matches any
                  uppercase character.
 (?[...])  [8]  Extended bracketed character class
 \w        [3]  Match a "word" character (alphanumeric plus "_", plus
                  other connector punctuation chars plus Unicode
                  marks)
 \W        [3]  Match a non-"word" character
 \s        [3]  Match a whitespace character
 \S        [3]  Match a non-whitespace character
 \d        [3]  Match a decimal digit character
 \D        [3]  Match a non-digit character
 \pP       [3]  Match P, named property.  Use \p{Prop} for longer names
 \PP       [3]  Match non-P
 \X        [4]  Match Unicode "eXtended grapheme cluster"
 \1        [5]  Backreference to a specific capture group or buffer.
                  '1' may actually be any positive integer.
 \g1       [5]  Backreference to a specific or previous group,
 \g{-1}    [5]  The number may be negative indicating a relative
                  previous group and may optionally be wrapped in
                  curly brackets for safer parsing.
 \g{name}  [5]  Named backreference
 \k<name>  [5]  Named backreference
 \k'name'  [5]  Named backreference
 \k{name}  [5]  Named backreference
 \K        [6]  Keep the stuff left of the \K, don't include it in $&
 \N        [7]  Any character but \n.  Not affected by /s modifier
 \v        [3]  Vertical whitespace
 \V        [3]  Not vertical whitespace
 \h        [3]  Horizontal whitespace
 \H        [3]  Not horizontal whitespace
 \R        [4]  Linebreak
[1]

Подробности см. в "Bracketed Character Classes" в perlrecharclass.

[2]

Подробности см. в "POSIX Character Classes" в perlrecharclass.

[3]

Подробности см. в "Unicode Character Properties" в perlunicode

[4]

Подробности см. в "Misc" в perlrebackslash.

[5]

Подробности см. ниже в разделе "Группы захвата".

[6]

Подробности см. ниже в разделе "Расширенные шаблоны".

[7]

Обратите внимание, что \N имеет два значения. Когда он имеет вид \N{NAME}, он соответствует символу или последовательности символов, имя которого ИМЯ; аналогично, когда он имеет вид \N{U+hex}, он соответствует символу с кодом Unicode hex. В противном случае он соответствует любому символу, кроме \n.

[8]

Подробности см. в "Extended Bracketed Character Classes" в perlrecharclass.

Утверждения

Помимо "^" и "$", Perl определяет следующие утверждения нулевой длины:

\b{}   Match at Unicode boundary of specified type
\B{}   Match where corresponding \b{} doesn't match
\b     Match a \w\W or \W\w boundary
\B     Match except at a \w\W or \W\w boundary
\A     Match only at beginning of string
\Z     Match only at end of string, or before newline at the end
\z     Match only at end of string
\G     Match only at pos() (e.g. at the end-of-match position
       of prior m//g)

Граница Юникода (\b{}), доступная начиная с версии 5.22, — это точка между двумя символами, или перед первым символом в строке, или после последнего символа в строке, где выполняются определенные критерии, определенные Юникодом. Подробности см. в "\b{}, \b, \B{}, \B" в perlrebackslash.

Граница слова (\b) — это точка между двумя символами, имеющая \w с одной стороны и \W с другой стороны (в любом порядке), при этом воображаемые символы в начале и конце строки рассматриваются как соответствующие \W. (Внутри классов символов \b представляет backspace, а не границу слова, как обычно в любой строке с двойными кавычками.) \A и \Z подобны "^" и "$", за исключением того, что они не будут соответствовать несколько раз при использовании модификатора /m, в то время как "^" и "$" будут соответствовать каждой внутренней границе строки. Чтобы соответствовать фактическому концу строки, а не игнорировать необязательный заключительный символ новой строки, используйте \z.

Утверждение \G можно использовать для цепочкового сопоставления глобальных совпадений (используя m//g), как описано в "Regexp Quote-Like Operators" в perlop. Оно также полезно при написании сканеров типа lex, когда у вас есть несколько шаблонов, которые вы хотите сопоставить с последовательными подстроками вашей строки; см. предыдущую ссылку. Фактическое положение, где \G будет сопоставлено, также может быть повлияно с помощью pos() в качестве lvalue: см. "pos" в perlfunc. Обратите внимание, что правило для соответствий нулевой длины (см. "Повторные шаблоны, сопоставляющие подстроку нулевой длины") несколько изменено, поскольку содержимое слева от \G не учитывается при определении длины совпадения. Таким образом, следующее не будет соответствовать бесконечно:

my $string = 'ABC';
pos($string) = 1;
while ($string =~ /(.\G)/g) {
    print $1;
}

Он выведет 'A' и затем завершится, поскольку рассматривает соответствие как нулевой ширины и поэтому не будет сопоставляться в одном и том же положении дважды подряд.

Стоит отметить, что \G при неправильном использовании может привести к бесконечному циклу. Будьте осторожны при использовании шаблонов, включающих \G в чередовании.

Также обратите внимание, что s/// откажется от перезаписи части замены, которая уже была заменена; так, например, это остановится после первой итерации, а не пройдёт итерацию назад по строке:

$_ = "123456789";
pos = 6;
s/.(?=.\G)/X/g;
print;      # prints 1234X6789, not XXXXX6789

Группы захвата

Конструктор группирования ( ... ) создает группы захвата (также называемые буферами захвата). Чтобы сослаться на текущее содержимое группы позже, в том же шаблоне, используйте \g1 (или \g{1}) для первой, \g2 (или \g{2}) для второй и так далее. Это называется ссылкой на обратную ссылку. Нет ограничений на количество захваченных подстрок, которые вы можете использовать. Группы нумеруются, начиная с самой левой открывающей скобки, которая имеет номер 1 и так далее. Если группа не соответствовала, соответствующая обратная ссылка также не будет соответствовать. (Это может произойти, если группа необязательна или в другой ветви чередования.) Вы можете опустить "g", и написать "\1", и так далее, но с этой формой есть некоторые проблемы, описанные ниже.

Вы также можете сослаться на группы захвата относительно, используя отрицательное число, так что \g-1 и \g{-1} оба ссылаются на непосредственно предшествующую группу захвата, и \g-2 и \g{-2} оба ссылаются на группу перед ней. Например:

/
 (Y)            # group 1
 (              # group 2
    (X)         # group 3
    \g{-1}      # backref to group 3
    \g{-3}      # backref to group 1
 )
/x

будет соответствовать тому же, что и /(Y) ( (X) \g3 \g1 )/x. Это позволяет вам интерполировать регулярные выражения в более крупные регулярные выражения и не беспокоиться о повторной нумерации групп захвата.

Вы можете обойтись без номеров полностью и создать именованные группы захвата. Нотация — (?<name>...) для объявления и \g{name} для ссылки. (Для совместимости с регулярными выражениями .Net, \g{name} также может быть записан как \k{name}, \k<name> или \k'name'.) Имя не должно начинаться с цифры и не должно содержать тире. Когда разные группы в одном шаблоне имеют одинаковое имя, любая ссылка на это имя подразумевает самую левую определённую группу. Именованные группы учитываются в абсолютной и относительной нумерации и, таким образом, могут быть также упомянуты с помощью этих чисел. (Существуют возможности работы с именованными группами захвата, которые в противном случае потребовали бы (??{}).)

Содержимое группы захвата динамически ограничено и доступно вам за пределами шаблона до конца вложенного блока или до следующего успешного совпадения, что произойдет первым. (См. "Compound Statements" в perlsyn.) Вы можете обратиться к ним по абсолютному номеру (используя "$1" вместо "\g1", и так далее); или по имени через хеш %+, используя "$+{name}".

Фигурные скобки требуются при ссылке на именованные группы захвата, но являются необязательными для абсолютных или относительных пронумерованных групп. Фигурные скобки более безопасны при создании регулярного выражения путем конкатенации меньших строк. Например, если у вас есть qr/$a$b/, и $a содержало "\g1", и $b содержало "37", вы получите /\g137/, что, вероятно, не является тем, что вы имели в виду.

Если вы используете фигурные скобки, вы также можете по желанию добавить любое количество пробелов (пробелы или табуляции) внутри, но рядом с фигурными скобками, например \g{ -1 } или \k{ name }.

Обозначения \g и \k были введены в Perl 5.10.0. До этого именованные или относительные пронумерованные группы захвата не существовали. Абсолютно пронумерованные группы ссылались с помощью \1, \2, и так далее, и эта запись всё ещё поддерживается (и, вероятно, всегда будет). Но это приводит к некоторым неоднозначностям, если есть более 9 групп захвата, поскольку \10 может означать либо десятую группу захвата, либо символ, порядковый номер которого в восьмеричной системе счисления 010 (пробел в ASCII). Perl устраняет эту неоднозначность, интерпретируя \10 как ссылку на обратную ссылку только в том случае, если перед ней открыто не менее 10 левых скобок. Аналогично, \11 является обратной ссылкой только в том случае, если перед ней открыто не менее 11 левых скобок. И так далее. \1 до \9 всегда интерпретируются как ссылки на обратную ссылку. Ниже приведены несколько примеров, иллюстрирующих эти опасности. Вы можете избежать неоднозначности, всегда используя \g{} или \g, если вы имеете в виду группы захвата; и для восьмеричных констант всегда используйте \o{}, или для \077 и ниже используйте 3 цифры, заполненные ведущими нулями, поскольку ведущий ноль предполагает восьмеричную константу.

Запись \digit также работает в определенных ситуациях вне шаблона. Подробности см. в разделе "Предупреждение о \1 вместо $1" ниже.

Примеры:

s/^([^ ]*) *([^ ]*)/$2 $1/;     # swap first two words

/(.)\g1/                        # find first doubled char
     and print "'$1' is the first doubled character\n";

/(?<char>.)\k<char>/            # ... a different way
     and print "'$+{char}' is the first doubled character\n";

/(?'char'.)\g1/                 # ... mix and match
     and print "'$1' is the first doubled character\n";

if (/Time: (..):(..):(..)/) {   # parse out values
    $hours = $1;
    $minutes = $2;
    $seconds = $3;
}

/(.)(.)(.)(.)(.)(.)(.)(.)(.)\g10/   # \g10 is a backreference
/(.)(.)(.)(.)(.)(.)(.)(.)(.)\10/    # \10 is octal
/((.)(.)(.)(.)(.)(.)(.)(.)(.))\10/  # \10 is a backreference
/((.)(.)(.)(.)(.)(.)(.)(.)(.))\010/ # \010 is octal

$a = '(.)\1';        # Creates problems when concatenated.
$b = '(.)\g{1}';     # Avoids the problems.
"aa" =~ /${a}/;      # True
"aa" =~ /${b}/;      # True
"aa0" =~ /${a}0/;    # False!
"aa0" =~ /${b}0/;    # True
"aa\x08" =~ /${a}0/;  # True!
"aa\x08" =~ /${b}0/;  # False

Несколько специальных переменных также ссылаются на части предыдущего совпадения. $+ возвращает то, что соответствовало последней паре скобок. $& возвращает всю сопоставленную строку. (В какой-то момент $0 также делал это, но теперь он возвращает имя программы.) $` возвращает все, что перед сопоставленной строкой. $' возвращает все, что после сопоставленной строки. И $^N содержит то, что было сопоставлено последней закрытой группой (подсовпадением). $^N может использоваться в расширенных шаблонах (см. ниже), например, для присвоения подсовпадения переменной.

Эти специальные переменные, как хеш %+ и пронумерованные переменные соответствия ($1, $2, $3, и так далее) динамически ограничены до конца окружающего блока или до следующего успешного совпадения, что произойдет первым. (См. "Compound Statements" в perlsyn.)

Массив @{^CAPTURE} может использоваться для доступа ко ВСЕМ буферам захвата как к массиву, не зная, сколько их. Например

$string=~/$pattern/ and @captured = @{^CAPTURE};

разместит копию каждой переменной захвата, $1, $2 и так далее, в массив @captured.

Обратите внимание, что при интерполяции индекса массива @{^CAPTURE} вы должны использовать обозначение фигурных скобок:

print "@{^CAPTURE[0]}";

См. "Размеченные имена переменных, использующие фигурные скобки" в perldata для получения дополнительной информации об этой нотации.

ПРИМЕЧАНИЕ: Неуспешные совпадения в Perl не сбрасывают переменные совпадения, что упрощает написание кода, который проверяет ряд более конкретных случаев и запоминает лучшее совпадение.

ПРЕДУПРЕЖДЕНИЕ: Если ваш код предназначен для выполнения на Perl 5.16 или более ранних версиях, имейте в виду, что как только Perl увидит, что вам нужен один из $&, $`, или $' где-либо в программе, он должен предоставить их для каждого соответствия шаблона. Это может существенно замедлить вашу программу.

Perl использует тот же механизм для создания $1, $2, и т.д., поэтому вы также платите за каждый шаблон, содержащий группирующие скобки. (Чтобы избежать этой стоимости, сохранив при этом поведение группирования, используйте расширенное регулярное выражение (?: ... ) вместо него.) Но если вы никогда не используете $&, $` или $', то шаблоны без группирующих скобок не будут подвержены штрафу. Поэтому избегайте $&, $', и $`, если можете, но если не можете (и некоторые алгоритмы их очень ценят), как только вы их использовали один раз, используйте их по желанию, потому что вы уже заплатили за это.

Perl 5.16 ввёл немного более эффективный механизм, который отдельно отмечает, были ли видны каждый из $`, $&, и $', и, таким образом, может потребоваться скопировать только часть строки. Perl 5.20 представил гораздо более эффективный механизм копирования при записи, который устраняет любые замедления.

В качестве другого обходного решения для этой проблемы, Perl 5.10.0 представил ${^PREMATCH}, ${^MATCH} и ${^POSTMATCH}, которые эквивалентны $`, $& и $', за исключением того, что они гарантированы, только если выполнено успешное совпадение, которое было выполнено с модификатором /p (сохранение). Использование этих переменных не влечёт за собой глобальных потерь производительности, в отличие от их эквивалентов из знаков препинания, однако ценой того, что вам нужно указать Perl, когда вы хотите их использовать. Начиная с Perl 5.20, эти три переменные эквивалентны $`, $& и $', и /p игнорируется.

Выделение метасимволов

Обрамлённые обратной косой чертой метасимволы в Perl являются буквенно-цифровыми, такими как \b, \w, \n. В отличие от некоторых других языков регулярных выражений, нет обрамлённых обратной косой чертой символов, которые не являются буквенно-цифровыми. Поэтому всё, что выглядит как \\, \(, \), \[, \], \{, или \} всегда интерпретируется как буквальный символ, а не метасимвол. Это раньше использовалось в общем выражении для отключения или выделения специальных значений метасимволов регулярных выражений в строке, которую вы хотите использовать в качестве шаблона. Просто выделите все символы, не являющиеся "словообразующими":

$pattern =~ s/(\W)/\\$1/g;

(Если use locale установлено, то это зависит от текущего региональных настроек.) Сегодня более распространённым является использование функции quotemeta() или последовательности метавыделения \Q для отключения всех специальных значений метасимволов таким образом:

/$unquoted\Q$quoted\E$unquoted/

Обратите внимание, что если вы помещаете буквальные обратные косые черты (те, что не находятся внутри интерполированных переменных) между \Q и \E, интерполяция двойной косой черты может привести к путанице. Если вам необходимо использовать буквальные обратные косые черты внутри \Q...\E, обратитесь к "Подробности синтаксического разбора выражений в кавычках" в perlop.

quotemeta() и \Q подробно описаны в "quotemeta" в perlfunc.

Расширенные шаблоны

Perl также определяет согласованный синтаксис расширений для функций, отсутствующих в стандартных инструментах, таких как awk и lex. Синтаксис для большинства из них - пара скобок с вопросительным знаком как первым символом внутри скобок. Символ после вопросительного знака указывает расширение.

Вопросник был выбран для этого и для конструкции минимального соответствия, потому что 1) вопросительные знаки редки в старых регулярных выражениях, и 2) всякий раз, когда вы видите его, вы должны остановиться и "задать вопрос", что происходит. Это психология…

(?#text)

Комментарий. Текст текст игнорируется. Обратите внимание, что Perl закрывает комментарий, как только видит ")", поэтому нет возможности поместить литерал ")" в комментарий. Закрывающая разделитель шаблона необходимо экранировать обратной косой чертой, если она появляется в комментарии.

См. "/x" для другого способа вставки комментариев в шаблоны.

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

qr/foo(?#comment)bar/'  # Matches 'foobar'

# The pattern below matches 'abcd', 'abccd', or 'abcccd'
qr/abc(?#comment between literal and its quantifier){1,3}d/

# The pattern below generates a syntax error, because the '\p' must
# be followed immediately by a '{'.
qr/\p(?#comment between \p and its property name){Any}/

# The pattern below generates a syntax error, because the initial
# '\(' is a literal opening parenthesis, and so there is nothing
# for the  closing ')' to match
qr/\(?#the backslash means this isn't a comment)p{Any}/

# Comments can be used to fold long patterns into multiple lines
qr/First part of a long regex(?#
  )remaining part/
(?adlupimnsx-imnsx)
(?^alupimnsx)

Ноль или более встроенных модификаторов совпадения шаблонов, которые необходимо включить (или отключить, если перед ними стоит "-") для оставшейся части шаблона или оставшейся части вложенной группы шаблонов (если таковая имеется).

Это особенно полезно для динамически сгенерированных шаблонов, таких как шаблоны, считываемые из файла конфигурации, из аргумента или указанные в таблице. Рассмотрим случай, когда некоторые шаблоны должны быть регистрозависимыми, а другие — нет: те, которые не учитывают регистр, просто должны включать (?i) в начало шаблона. Например:

$pattern = "foobar";
if ( /$pattern/i ) { }

# more flexible:

$pattern = "(?i)foobar";
if ( /$pattern/ ) { }

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

( (?i) blah ) \s+ \g1

сопоставит blah в любом регистре, некоторые пробелы и точное (включая регистр!) повторение предыдущего слова, предполагая модификатор /x, и отсутствие модификатора /i вне этой группы.

Эти модификаторы не переносятся во вложенные подшаблоны, вызываемые во внешней группе. Другими словами, такой шаблон, как ((?i)(?&NAME)) не изменяет регистрозависимость шаблона NAME.

Модификатор переопределяется последующими появлениями этого конструкта в том же объеме, содержащем тот же модификатор, так что

/((?im)foo(?-m)bar)/

сопоставляет все из foobar без учета регистра, но использует правила /m только для части foo. Флаг "a" переопределяет aa, аналогично aa переопределяет "a". То же самое относится к "x" и xx. Следовательно, в

/(?-x)foo/xx

оба /x и /xx отключаются во время сопоставления foo. И в

/(?x)foo/x

/x но НЕ /xx включено для сопоставления foo. (Можно ошибочно подумать, что поскольку внутренний (?x) уже находится в области действия /x, что результат фактически будет суммой их, что даст /xx. Это не так.) Аналогично, выполнение чего-либо вроде (?xx-x)foo отключает все поведение "x" для сопоставления foo, это не значит, что вы вычитаете 1 "x" из 2, чтобы получить 1 "x" оставшихся.

Любой из этих модификаторов может быть установлен для глобального применения ко всем регулярным выражениям, скомпилированным в области действия use re. См. "'/flags' mode" в re.

Начиная с Perl 5.14, "^" (знак возвышения или острые скобки) сразу после "?" — это сокращенная форма записи, эквивалентная d-imnsx. Флаги (кроме "d") могут следовать за знаком возвышения, чтобы переопределить его. Но знак минуса с ним не допустим.

Обратите внимание, что модификаторы "a", "d", "l", "p", и "u" являются специальными, так как их можно только включить, но не отключить, а модификаторы "a", "d", "l", и "u" взаимоисключают друг друга: указание одного исключает другие, и может появиться не более одного (или двух "a"). Например, (?-p) выведет предупреждение при компиляции в use warnings; (?-d:...) и (?dl:...) — это фатальные ошибки.

Обратите также внимание, что модификатор "p" является специальным тем, что его присутствие в любом месте шаблона имеет глобальное значение.

Отсутствие модификаторов делает это бесполезной операцией (так зачем вы это указали, если это не сгенерированный код?), а начиная с v5.30, выводит предупреждение в use re 'strict'.

(?:pattern)
(?adluimnsx-imnsx:pattern)
(?^aluimnsx:pattern)

Это предназначено для группирования, а не захвата; оно группирует подвыражения, как "()", но не создает обратные ссылки, как "()". Так что

@fields = split(/\b(?:a|b|c)\b/)

сопоставляет те же разделители полей, что и

@fields = split(/\b(a|b|c)\b/)

но не выводит сами разделители как дополнительные поля (хотя это поведение "split" в perlfunc, когда его шаблон содержит группы захвата). Также дешевле не захватывать символы, если вам они не нужны.

Любые буквы между "?" и ":" действуют как флаги-модификаторы, как и в (?adluimnsx-imnsx). Например,

/(?s-i:more.*than).*million/i

эквивалентно более длинной записи

/(?:(?s-i)more.*than).*million/i

Обратите внимание, что любые () конструкции, заключённые в этой, всё ещё будут захватывать, если не активен модификатор /n.

Как и конструкция "(?adlupimnsx-imnsx)", aa и "a" переопределяют друг друга, как и xx и "x". Они не являются аддитивными. Так что, выполнение чего-либо вроде (?xx-x:foo) отключает все поведение "x" для сопоставления foo.

Начиная с Perl 5.14, "^" (знак возвышения или острые скобки) сразу после "?" — это сокращенная форма записи, эквивалентная d-imnsx. Любые положительные флаги (кроме "d") могут следовать за знаком возвышения, поэтому

(?^x:foo)

эквивалентно

(?x-imns:foo)

Знак возвышения сообщает Perl, что эта группа не наследует флаги окружающего шаблона, а использует системные значения по умолчанию (d-imnsx), изменённые любыми указанными флагами.

Знак возвышения позволяет упростить строковое представление скомпилированных регулярных выражений. Они выглядят как

(?^:pattern)

с любыми флагами, отличными от значений по умолчанию, появляющимися между знаком возвышения и двоеточием. Тест, который рассматривает такое строковое представление, поэтому не нуждается в жёстком кодировании системных значений по умолчанию, а только в знаке возвышения. Если к Perl будут добавлены новые флаги, значение расширения знака возвышения будет изменено, чтобы включить значения по умолчанию для этих флагов, поэтому тест по-прежнему будет работать неизменённым.

Указание отрицательного флага после знака возвышения является ошибкой, так как флаг избыточен.

Мнемоника для (?^...): Новое начало, поскольку обычное использование знака возвышения — соответствие в начале.

(?|pattern)

Это шаблон «сброс ветвей», который обладает особым свойством, заключающимся в том, что группы захвата нумеруются с одной и той же начальной точки в каждом альтернативном ветвящемся подшаблоне. Доступен начиная с perl 5.10.0.

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

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

Этот конструкт полезен, когда вы хотите захватить один из нескольких альтернативных совпадений.

Рассмотрим следующий шаблон. Цифры под ним показывают, в какой группе будет храниться захваченное содержимое.

# before  ---------------branch-reset----------- after
/ ( a )  (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
# 1            2         2  3        2     3     4

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

/(?|  (?<a> x ) (?<b> y )
   |  (?<a> z ) (?<b> w )) /x

Если этого не сделать, могут возникнуть неожиданные результаты:

"12" =~ /(?| (?<a> \d+ ) | (?<b> \D+))/x;
say $+{a};    # Prints '12'
say $+{b};    # *Also* prints '12'.

Проблема здесь в том, что обе группы, названные a и b, являются псевдонимами группы, принадлежащей $1.

Операторы проверки окружения

Утверждения lookaround — это шаблоны нулевой ширины, которые соответствуют определённому шаблону, не включая его в $&. Положительные утверждения соответствуют, когда их подшаблон соответствует, отрицательные утверждения соответствуют, когда их подшаблон не соответствует. Lookbehind соответствует тексту до текущей позиции соответствия, lookahead соответствует тексту после текущей позиции соответствия.

(?=pattern)
(*pla:pattern)
(*positive_lookahead:pattern)

Позитивное утверждение lookahead нулевой ширины. Например, /\w+(?=\t)/ соответствует слову, за которым следует табуляция, без включения табуляции в $&.

(?!pattern)
(*nla:pattern)
(*negative_lookahead:pattern)

Отрицательное утверждение lookahead нулевой ширины. Например, /foo(?!bar)/ соответствует любому вхождению «foo», за которым не следует «bar». Обратите внимание, что lookahead и lookbehind — это НЕ одно и то же. Вы не можете использовать это для lookbehind.

Если вы ищете «bar», за которым не следует «foo», /(?!foo)bar/ не сделает того, что вы хотите. Это потому, что (?!foo) просто говорит, что следующее не может быть «foo» — а это не так, это «bar», поэтому «foobar» будет соответствовать. Используйте lookbehind вместо этого (см. ниже).

(?<=pattern)
\K
(*plb:pattern)
(*positive_lookbehind:pattern)

Позитивное утверждение lookbehind нулевой ширины. Например, /(?<=\t)\w+/ соответствует слову, за которым следует табуляция, без включения табуляции в $&.

До Perl 5.30 он работал только с lookbehind фиксированной ширины, но начиная с этого релиза, он может обрабатывать переменные длины от 1 до 255 символов в качестве экспериментальной функции. Эта функция включается автоматически, если вы используете утверждение lookbehind с переменной длиной.

В Perl 5.35.10 область экспериментального характера этого конструкта была уменьшена, и экспериментальные предупреждения будут выдаваться только тогда, когда констукт содержит захватывающие скобки. Предупреждения будут выведены во время компиляции шаблона, если они не отключены, в категории experimental::vlb. Это предупреждение о том, что точное содержимое буферов захвата в утверждении lookbehind переменной длины не определено и может измениться в будущих версиях Perl.

В настоящее время, если вы используете буферы захвата внутри положительного lookbehind переменной длины, результатом будет самое длинное и, следовательно, самое левое возможное совпадение. Это означает, что

"aax" =~ /(?=x)(?<=(a|aa))/
"aax" =~ /(?=x)(?<=(aa|a))/
"aax" =~ /(?=x)(?<=(a{1,2}?)/
"aax" =~ /(?=x)(?<=(a{1,2})/

все приведут к $1 содержащему "aa". Возможно, в будущих версиях Perl мы изменим это поведение.

Существует специальная форма этого конструкта, называемая \K (доступна начиная с Perl 5.10.0), которая заставляет движок регулярных выражений «сохранять» всё, что он сопоставил до \K и не включать это в $&. Это эффективно обеспечивает неэкспериментальный lookbehind переменной длины любой длины.

И существует метод, который можно использовать для обработки lookbehind переменной длины в более ранних версиях и длиной более 255 символов. Он описан в http://www.drregex.com/2019/02/variable-length-lookbehinds-actually.html.

Обратите внимание, что в /i, несколько одиночных символов соответствуют двум или трём другим символам. Это делает их переменной длины, а длина 255 относится к максимальному числу символов в совпадении. Например, qr/\N{LATIN SMALL LETTER SHARP S}/i соответствует последовательности "ss". Ваше утверждение lookbehind может содержать 127 символов Sharp S в /i, но добавление 128-го вызовет ошибку компиляции, так как это может соответствовать 256 "s" символам подряд.

Использование \K внутри другого утверждения lookaround разрешено, но поведение в настоящее время не определено.

По различным причинам \K может быть значительно эффективнее эквивалентного (?<=...) конструкта, и он особенно полезен в ситуациях, когда вы хотите эффективно удалить что-то, идущее за чем-то другим в строке. Например

s/(foo)bar/$1/g;

можно переписать как намного более эффективное

s/foo\Kbar//g;

Использование модификатора нежадного соответствия "?" может не дать ожидаемых результатов, если он находится внутри группы захвата в данном конструкте.

(?<!pattern)
(*nlb:pattern)
(*negative_lookbehind:pattern)

Отрицательное утверждение lookbehind нулевой ширины. Например, /(?<!bar)foo/ соответствует любому вхождению «foo», за которым не следует «bar».

До Perl 5.30 он работал только с lookbehind фиксированной ширины, но начиная с этого релиза, он может обрабатывать переменные длины от 1 до 255 символов в качестве экспериментальной функции. Эта функция включается автоматически, если вы используете утверждение lookbehind с переменной длиной.

В Perl 5.35.10 область экспериментального характера этого конструкта была уменьшена, и экспериментальные предупреждения будут выдаваться только тогда, когда констукт содержит захватывающие скобки. Предупреждения будут выведены во время компиляции шаблона, если они не отключены, в категории experimental::vlb. Это предупреждение о том, что точное содержимое буферов захвата в утверждении lookbehind переменной длины не определено и может измениться в будущих версиях Perl.

В настоящее время, если вы используете буферы захвата внутри отрицательного lookbehind переменной длины, результат может не быть тем, что вы ожидаете, например:

say "axfoo"=~/(?=foo)(?<!(a|ax)(?{ say $1 }))/ ? "y" : "n";

выведет следующее:

a
no

что не имеет смысла, так как это должно вывести «ax», так как «а» не совпадает в нужном месте. Ещё один пример:

say "yes: '$1-$2'" if "aayfoo"=~/(?=foo)(?<!(a|aa)(a|aa)x)/;

выведет следующее:

yes: 'aa-a'

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

Мы уверены, что констукт правильно сопоставляет и отклоняет шаблоны, неопределённое поведение напрямую связано со значением буфера захвата во время или после сопоставления.

Существует метод, который можно использовать для обработки lookbehind переменной длины в более ранних версиях и длиной более 255 символов. Он описан в http://www.drregex.com/2019/02/variable-length-lookbehinds-actually.html.

Обратите внимание, что в /i, несколько одиночных символов соответствуют двум или трём другим символам. Это делает их переменной длины, а длина 255 относится к максимальному числу символов в совпадении. Например, qr/\N{LATIN SMALL LETTER SHARP S}/i соответствует последовательности "ss". Ваше утверждение lookbehind может содержать 127 символов Sharp S в /i, но добавление 128-го вызовет ошибку компиляции, так как это может соответствовать 256 "s" символам подряд.

Использование модификатора нежадного соответствия "?" может не дать ожидаемых результатов, если он находится внутри группы захвата в данном конструкте.

(?<NAME>pattern)
(?'NAME'pattern)

Именованная группа захвата. Полностью идентична обычным захватывающим скобкам (), за исключением того, что к группе можно обратиться по имени в различных конструкциях регулярных выражений (например, \g{NAME}) и получить доступ к ней по имени после успешного совпадения через %+ или %-. Подробнее о массивах %+ и %- см. в perlvar.

Если у нескольких различных групп захвата одинаковое имя, то $+{NAME} будет ссылаться на самую левую определённую группу в совпадении.

Формы (?'NAME'pattern) и (?<NAME>pattern) эквивалентны.

ПРИМЕЧАНИЕ: Хотя запись этого конструкта такая же, как и у подобной функции в регулярных выражениях .NET, поведение отличается. В Perl группы нумеруются последовательно независимо от именованности. Таким образом, в шаблоне

/(x)(?<foo>y)(z)/

$+{foo} будет таким же, как и $2, а $3 будет содержать 'z' вместо обратного, чего может ожидать хакер с регулярными выражениями .NET.

В настоящее время NAME ограничен только простыми идентификаторами. Другими словами, он должен соответствовать /^[_A-Za-z][_A-Za-z0-9]*\z/ или его расширению Юникода (см. utf8), хотя он не расширяется локалью (см. perllocale).

ПРИМЕЧАНИЕ: Чтобы облегчить программистам с опытом работы с движками регулярных выражений Python или PCRE, может быть использован шаблон (?P<NAME>pattern) вместо (?<NAME>pattern); однако эта форма не поддерживает использование одинарных кавычек в качестве разделителя для имени.

\k<NAME>
\k'NAME'
\k{NAME}

Именованная ссылка на обратную ссылку. Похожа на числовые обратные ссылки, за исключением того, что группа обозначается именем, а не номером. Если у нескольких групп одинаковое имя, то это ссылается на самую левую определённую группу в текущем совпадении.

Ошибка, если ссылаться на имя, которое не определено в (?<NAME>) ранее в шаблоне.

Все три формы эквивалентны, хотя с \k{ NAME }, вы можете необязательно иметь пробелы внутри, но рядом с фигурными скобками, как показано.

ПРИМЕЧАНИЕ: Чтобы облегчить программистам с опытом работы с движками регулярных выражений Python или PCRE, может быть использован шаблон (?P=NAME) вместо \k<NAME>.

(?{ code })

ПРЕДУПРЕЖДЕНИЕ: Безопасное использование этой функции требует понимания её ограничений. Код, выполняемый с побочными эффектами, может вести себя по-разному в разных версиях из-за оптимизаций регулярного выражения в будущих версиях. Для получения дополнительной информации см. "Частота выполнения встроенного кода".

Это утверждение нулевой ширины выполняет любой встроенный Perl-код. Оно всегда выполняется успешно, а его возвращаемое значение устанавливается как $^R.

В литеральных шаблонах код парсится одновременно с окружающим кодом. Внутри шаблона управление временно передаётся обратно парсеру Perl, пока не будет встречена логически соответствующая закрывающая фигурная скобка. Это аналогично тому, как обрабатывается выражение индекса массива в строке-литерале, например:

"abc$array[ 1 + f('[') + g()]def"

В частности, фигурные скобки не обязательно должны быть сбалансированы:

s/abc(?{ f('{'); })/def/

Даже в шаблоне, который интерполируется и компилируется во время выполнения, блочные литералы будут скомпилированы один раз во время компиляции Perl; следующее выведет "ABCD":

print "D";
my $qr = qr/(?{ BEGIN { print "A" } })/;
my $foo = "foo";
/$foo$qr(?{ BEGIN { print "B" } })/;
BEGIN { print "C" }

В шаблонах, где текст кода получен из информации времени выполнения, а не представлен буквально в исходном коде/шаблоне, код компилируется одновременно с компиляцией шаблона, и по соображениям безопасности use re 'eval' должен быть в области видимости. Это делается для предотвращения выполнения кода, содержащегося в пользовательских шаблонах.

В ситуациях, когда вам нужно включить это с use re 'eval', вы также должны включить проверку на загрязнение, если ваш Perl её поддерживает. Ещё лучше использовать тщательно ограниченное вычисление внутри безопасного отсека. См. perlsec для получения подробной информации об этих механизмах.

С точки зрения парсинга, области видимости лексических переменных и замыканий,

/AAA(?{ BBB })CCC/

ведет себя примерно как

/AAA/ && do { BBB } && /CCC/

Аналогично,

qr/AAA(?{ BBB })CCC/

ведет себя примерно как

sub { /AAA/ && do { BBB } && /CCC/ }

В частности:

{ my $i = 1; $r = qr/(?{ print $i })/ }
my $i = 2;
/$r/; # prints "1"

Внутри блока (?{...}), $_ ссылается на строку, по которой ищется совпадение регулярного выражения. Вы также можете использовать pos() для определения текущей позиции совпадения в этой строке.

Блок кода вводит новую область видимости с точки зрения объявления лексических переменных, но не с точки зрения local и подобных локальных действий. Таким образом, последующие блоки кода в том же шаблоне по-прежнему будут видеть значения, которые были локализованы в более ранних блоках. Эти накопленные локализации отменяются либо в конце успешного совпадения, либо если утверждение отменено обратной подстановкой (сравните "Обратная подстановка"). Например,

$_ = 'a' x 8;
m<
   (?{ $cnt = 0 })               # Initialize $cnt.
   (
     a
     (?{
         local $cnt = $cnt + 1;  # Update $cnt,
                                 # backtracking-safe.
     })
   )*
   aaaa
   (?{ $res = $cnt })            # On success copy to
                                 # non-localized location.
 >x;

сначала увеличит $cnt до 8; затем во время обратной подстановки его значение будет уменьшено обратно до 4, что является значением, присвоенным $res. В конце выполнения регулярного выражения $cnt вернётся к своему начальному значению 0.

Это утверждение может использоваться в качестве условия в

(?(condition)yes-pattern|no-pattern)

переключателе. Если оно не используется таким образом, результат оценки code помещается в специальную переменную $^R. Это происходит немедленно, поэтому $^R можно использовать из других (?{ code }) утверждений внутри того же регулярного выражения.

Присвоение $^R выше должным образом локализовано, поэтому старое значение $^R восстанавливается, если утверждение отменено обратной подстановкой; сравните "Обратная подстановка".

Обратите внимание, что специальная переменная $^N особенно полезна при использовании блоков кода для захвата результатов подстрочных совпадений в переменных без необходимости отслеживания количества вложенных скобок. Например:

$_ = "The brown fox jumps over the lazy dog";
/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;
print "color = $color, animal = $animal\n";
(??{ code })

ПРЕДУПРЕЖДЕНИЕ: Безопасное использование этой функции требует понимания её ограничений. Код, выполняемый с побочными эффектами, может вести себя по-разному в разных версиях из-за оптимизаций регулярного выражения в будущих версиях. Для получения дополнительной информации см. "Частота выполнения встроенного кода".

Это "отложенное" подвыражение регулярного выражения. Оно ведет себя точно так же, как блок кода (?{ code }), как описано выше, за исключением того, что его возвращаемое значение, вместо того, чтобы быть присвоенным $^R, обрабатывается как шаблон, компилируется, если это строка (или используется как есть, если это qr// объект), а затем проверяется на соответствие, как если бы оно было вставлено вместо этого конструкта.

Во время проверки подвыражения на соответствие у него есть свой набор захватов, которые действительны во время проверки подвыражения, но отбрасываются после возврата управления в основной шаблон. Например, следующее сопоставляется, при этом внутренний шаблон захватывает "B" и сопоставляет "BB", в то время как внешний шаблон захватывает "A";

my $inner = '(.)\1';
"ABBA" =~ /^(.)(??{ $inner })\1/;
print $1; # prints "A";

Обратите внимание, что это означает, что внутренний шаблон не может ссылаться на группу захвата, определенную снаружи. (Сам блок кода может использовать $1, и т.д., для ссылки на группы захвата окружающего шаблона.) Таким образом, хотя

('a' x 100)=~/(??{'(.)' x 100})/

будет соответствовать, оно не установит $1 при выходе.

Следующий шаблон соответствует скобочной группе:

$re = qr{
           \(
           (?:
              (?> [^()]+ )  # Non-parens without backtracking
            |
              (??{ $re })   # Group with matching parens
           )*
           \)
        }x;

См. также (?PARNO) для другого, более эффективного способа достижения той же цели.

Выполнение отложенного регулярного выражения слишком много раз без потребления входной строки также приведет к ошибке. Глубина, на которой это происходит, скомпилирована в Perl, поэтому её можно изменить с помощью кастомной сборки.

(?PARNO) (?-PARNO) (?+PARNO) (?R) (?0)

Рекурсивное подвыражение. Обработать содержимое заданного буфера захвата в текущем шаблоне как независимое подвыражение и попытаться сопоставить его в текущей позиции в строке. Информация о состоянии захвата от вызывающего элемента, например, для обратных ссылок, доступна подвыражению, но буферы захвата, установленные подвыражением, недоступны вызывающему элементу.

Подобно (??{ code }), за исключением того, что оно не включает выполнения кода или потенциальной компиляции возвращаемой строки шаблона; вместо этого оно обрабатывает часть текущего шаблона, содержащуюся внутри указанной группы захвата, как независимый шаблон, который должен соответствовать в текущей позиции. Отличие также заключается в обработке буферов захвата, в отличие от (??{ code }), рекурсивные шаблоны имеют доступ к состоянию соответствия вызывающего элемента, поэтому можно безопасно использовать обратные ссылки.

PARNO — это последовательность цифр (не начинающаяся с 0), значение которой отражает номер скобки группы захвата, к которой требуется рекурсия. (?R) возвращается к началу всего шаблона. (?0) — это альтернативная синтаксическая конструкция для (?R). Если перед PARNO стоит знак плюс или минус, то предполагается, что он относительный, причём отрицательные числа указывают на предыдущие группы захвата, а положительные — на последующие. Таким образом, (?-1) ссылается на последнюю объявленную группу, а (?+1) указывает на следующую группу для объявления. Обратите внимание, что счёт для относительной рекурсии отличается от учёта относительных обратных ссылок, так как при рекурсии не закрытые группы включаются.

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

$re = qr{ (                   # paren group 1 (full function)
            foo
            (                 # paren group 2 (parens)
              \(
                (             # paren group 3 (contents of parens)
                (?:
                 (?> [^()]+ ) # Non-parens without backtracking
                |
                 (?2)         # Recurse to start of paren group 2
                )*
                )
              \)
            )
          )
        }x;

Если шаблон использовался следующим образом

'foo(bar(baz)+baz(bop))'=~/$re/
    and print "\$1 = $1\n",
              "\$2 = $2\n",
              "\$3 = $3\n";

выведенный результат должен быть следующим:

$1 = foo(bar(baz)+baz(bop))
$2 = (bar(baz)+baz(bop))
$3 = bar(baz)+baz(bop)

Если соответствующей группы захвата не определено, возникает ошибка. Глубокая рекурсия без потребления входной строки также приведет к ошибке. Глубина, на которой это происходит, скомпилирована в Perl, поэтому её можно изменить с помощью кастомной сборки.

Ниже показано, как использование отрицательного индексирования может упростить встраивание рекурсивных шаблонов в конструкцию qr// для последующего использования:

my $parens = qr/(\((?:[^()]++|(?-1))*+\))/;
if (/foo $parens \s+ \+ \s+ bar $parens/x) {
   # do something here...
}

Примечание, что этот шаблон не ведет себя так же, как эквивалентный конструкт PCRE или Python того же вида. В Perl вы можете вернуться в рекурсивную группу, в PCRE и Python рекурсивно введённая группа обрабатывается как атомарная. Также модификаторы разрешаются во время компиляции, поэтому конструкции, подобные (?i:(?1)) или (?:(?i)(?1)), не влияют на обработку подвыражения.

(?&NAME)

Рекурсия к именованному подвыражению. Идентично (?PARNO) за исключением того, что скобка для рекурсии определяется по имени. Если несколько скобок имеют одинаковое имя, то рекурсия происходит к самой левой.

Ссылка на имя, которое не объявлено где-либо в шаблоне, является ошибкой.

ПРИМЕЧАНИЕ: Для упрощения работы программистов, знакомых с движками регулярных выражений Python или PCRE, может использоваться шаблон (?P>NAME) вместо (?&NAME).

(?(condition)yes-pattern|no-pattern)
(?(condition)yes-pattern)

Условное выражение. Сопоставляет yes-шаблон, если условие имеет истинное значение, иначе сопоставляет no-шаблон. Отсутствие шаблона всегда соответствует.

(condition) должно быть одним из:

целое число в скобках

(что является допустимым, если соответствующая пара скобок совпала);

утверждение о нулевой ширине для поиска вперёд/назад/оценки;
имя в угловых скобках или одинарных кавычках

(что является допустимым, если группа с данным именем совпала);

специальный символ (R)

(истина, когда вычисляется внутри рекурсии или eval). Кроме того, "R" может быть последует за числом, (что будет истинно при оценке при рекурсии внутри соответствующей группы), или за &NAME, в этом случае оно будет истинно только при оценке во время рекурсии в именованной группе.

Вот сводка возможных предикатов:

(1) (2) ...

Проверяет, сопоставлена ли какая-либо группа захвата с заданным номером. Полный синтаксис: (?(1)then|else)

(<NAME>) ('NAME')

Проверяет, сопоставлена ли какая-либо группа с данным именем. Полный синтаксис: (?(<name>)then|else)

(?=...) (?!...) (?<=...) (?<!...)

Проверяет, соответствует ли шаблон (или не соответствует, для вариантов "!"). Полный синтаксис: (?(?=lookahead)then|else)

(?{ CODE })

Обрабатывает возвращаемое значение блока кода как условие. Полный синтаксис: (?(?{ code })then|else)

(R)

Проверяет, вычислялось ли выражение внутри рекурсии. Полный синтаксис: (?(R)then|else)

(R1) (R2) ...

Проверяет, вычислялось ли выражение при непосредственном выполнении внутри n-й группы захвата. Эта проверка является регулярным выражением эквивалентом

if ((caller(0))[3] eq 'subname') { ... }

Другими словами, она не проверяет весь стек рекурсии.

Полный синтаксис: (?(R1)then|else)

(R&NAME)

Подобно (R1), этот предикат проверяет, выполняем ли мы непосредственно внутри левосторонней группы с заданным именем (эта же логика используется в (?&NAME) для устранения неоднозначности). Он не проверяет весь стек, а только имя самой внутренней активной рекурсии. Полный синтаксис: (?(R&name)then|else)

(DEFINE)

В этом случае yes-шаблон никогда напрямую не выполняется, и no-шаблон не допускается. По духу подобно (?{0}), но более эффективно. Подробности см. ниже. Полный синтаксис: (?(DEFINE)definitions...)

Например:

m{ ( \( )?
   [^()]+
   (?(1) \) )
 }x

сопоставляет кусок, не содержащий скобок, возможно, включённый в сами скобки.

Специальная форма — предикат (DEFINE), который никогда не выполняет свой yes-шаблон напрямую и не допускает no-шаблон. Это позволяет определять подшаблоны, которые будут выполняться только механизмом рекурсии. Таким образом, вы можете определить набор правил регулярных выражений, которые могут быть объединены в любой выбранный вами шаблон.

Рекомендуется размещать блок DEFINE в конце шаблона и именовать любые подшаблоны, определённые в нём.

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

Пример того, как это может использоваться, выглядит следующим образом:

/(?<NAME>(?&NAME_PAT))(?<ADDR>(?&ADDRESS_PAT))
 (?(DEFINE)
   (?<NAME_PAT>....)
   (?<ADDRESS_PAT>....)
 )/x

Обратите внимание, что группы захвата, сопоставленные внутри рекурсии, недоступны после возвращения из рекурсии, поэтому необходим дополнительный уровень групп захвата. Таким образом, $+{NAME_PAT} не будет определён, даже если $+{NAME} будет.

Наконец, имейте в виду, что подшаблоны, созданные внутри блока DEFINE, учитываются в абсолютном и относительном количестве захватов, поэтому это:

my @captures = "a" =~ /(.)                  # First capture
                       (?(DEFINE)
                           (?<EXAMPLE> 1 )  # Second capture
                       )/x;
say scalar @captures;

Выведет 2, а не 1. Это особенно важно, если вы планируете компилировать определения с оператором qr// и позже интерполировать их в другой шаблон.

(?>pattern)
(*atomic:pattern)

«Независимое» подвыражение, которое сопоставляет подстроку, которую сопоставил бы самостоятельный шаблон, если бы он был привязан к заданной позиции, и оно сопоставляет только эту подстроку. Этот конструкт полезен для оптимизации того, что в противном случае были бы «вечные» сопоставления, потому что он не будет обращаться к истории (см. "Обратный откат"). Он также может быть полезен в тех местах, где желаема семантика «захватить всё, что можно, и ничего не возвращать».

Например: ^(?>a*)ab никогда не будет соответствовать, так как (?>a*) (привязанное к началу строки, как выше) будет соответствовать всем символам "a" в начале строки, не оставляя "a" для ab для сопоставления. В отличие от этого, a*ab будет соответствовать тому же, что и a+b, так как соответствие подгруппы a* зависит от следующей группы ab (см. "Обратный откат"). В частности, a* внутри a*ab будет соответствовать меньшему количеству символов, чем самостоятельное a*, поскольку это делает сопоставление хвоста.

(?>pattern) не отключает обратный откат полностью после сопоставления. Возвращаться к конструкции всё ещё возможно, но не в неё. Так ((?>a*)|(?>b*))ar всё равно будет соответствовать «bar».

Эффект, подобный (?>pattern), может быть достигнут записью (?=(pattern))\g{-1}. Это соответствует той же подстроке, что и самостоятельное a+, а следующее \g{-1} съедает сопоставленную строку; следовательно, это преобразует утверждение нулевой длины в аналог (?>...). (Разница между этими двумя конструкциями заключается в том, что вторая использует группу захвата, тем самым смещая порядковые номера обратных ссылок в остальной части регулярного выражения.)

Рассмотрим этот шаблон:

m{ \(
      (
        [^()]+           # x+
      |
        \( [^()]* \)
      )+
   \)
 }x

Это эффективно соответствует непустой группе со соответствующими скобками глубиной не более двух уровней. Однако, если такой группы нет, это займёт практически вечность на длинной строке. Это происходит из-за большого количества различных способов разбить длинную строку на несколько подстрок. Именно это делает (.+)+, а (.+)+ подобно подшаблону шаблона выше. Подумайте, как шаблон выше обнаруживает несоответствие на ((()aaaaaaaaaaaaaaaaaa за несколько секунд, но каждый дополнительный символ удваивает это время. Эта экспоненциальная производительность заставит вас подумать, что ваша программа зависла. Однако, небольшое изменение этого шаблона

m{ \(
      (
        (?> [^()]+ )        # change x+ above to (?> x+ )
      |
        \( [^()]* \)
      )+
   \)
 }x

который использует (?>...), соответствует точно, как и предыдущий (проверить это самостоятельно будет полезным упражнением), но завершается в четыре раза быстрее при использовании на похожей строке с 1000000 "a". Однако будьте осторожны, так как когда эта конструкция следует за квантификатором, она в настоящее время генерирует сообщение об ошибке под прагмой use warnings или переключателем -w, сообщая, что она "matches null string many times in regex".

Для простых групп, таких как шаблон (?> [^()]+ ), аналогичный эффект можно получить с помощью отрицательного поиска вперёд, как в [^()]+ (?! [^()] ). Это было всего в 4 раза медленнее на строке с 1000000 "a".

Семантика «захватить всё, что можно, и ничего не возвращать» желательна во многих ситуациях, когда на первый взгляд простой ()* кажется правильным решением. Предположим, мы разбираем текст, где комментарии ограничены "#", за которым следует произвольное (горизонтальное) пробельное разбиение. Вопреки своему виду, #[ \t]* не является правильным подвыражением для сопоставления разделителя комментариев, поскольку он может «отказаться» от пробела, если остальная часть шаблона может быть сопоставлена таким образом. Правильным ответом является одно из следующих:

(?>#[ \t]*)
#[ \t]*(?![ \t])

Например, чтобы получить непустые комментарии в $1, следует использовать одно из следующих выражений:

/ (?> \# [ \t]* ) (        .+ ) /x;
/     \# [ \t]*   ( [^ \t] .* ) /x;

Какой из них вы выберете, зависит от того, какое из этих выражений лучше отражает вышеупомянутое описание комментариев.

В некоторых литературных источниках эта конструкция называется «атомарным сопоставлением» или «положительным сопоставлением».

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

Quantifier Form     Bracketing Form
---------------     ---------------
PAT*+               (?>PAT*)
PAT++               (?>PAT+)
PAT?+               (?>PAT?)
PAT{min,max}+       (?>PAT{min,max})

Вложенные (?>...) конструкции не являются пустыми операциями, даже если на первый взгляд они могут таковыми казаться. Это связано с тем, что вложенные (?>...) могут ограничивать внутренние обратные откаты, которые в противном случае могли бы произойти. Например,

"abc" =~ /(?>a[bc]*c)/

соответствует, но

"abc" =~ /(?>a(?>[bc]*)c)/

не соответствует.

(?[ ])

См. "Расширенные символьные классы в скобках" в perlrecharclass.

Обратный откат

ПРИМЕЧАНИЕ: Этот раздел представляет собой абстрактное приближение поведения регулярных выражений. Более строгий (и сложный) взгляд на правила, участвующие в выборе соответствия среди возможных альтернатив, см. "Объединение частей RE".

Фундаментальной особенностью сопоставления регулярных выражений является понятие «обратного отката», которое в настоящее время используется (по мере необходимости) всеми квантификаторами регулярных выражений без «захвата всего», а именно "*", *?, "+", +?, {n,m}, и {n,m}?. Обратный откат часто оптимизируется внутри, но общий принцип, изложенный здесь, верен.

Для того чтобы регулярное выражение соответствовало, вся часть регулярного выражения должна соответствовать, а не только часть. Итак, если начало шаблона, содержащего квантификатор, достигает успеха таким образом, что последующие части шаблона терпят неудачу, движок сопоставления возвращается назад и пересчитывает начальную часть — вот почему это называется обратным откатом.

Вот пример обратного отката: Предположим, вам нужно найти слово, следующее за «foo», в строке «Food is on the foo table»:

$_ = "Food is on the foo table.";
if ( /\b(foo)\s+(\w+)/i ) {
    print "$2 follows $1.\n";
}

Когда выполняется сопоставление, первая часть регулярного выражения (\b(foo)) находит возможный шаблон прямо в начале строки и загружает $1 с «Foo». Однако, как только движок сопоставления видит, что после «Foo», которое было сохранено в $1, нет пробела, он понимает свою ошибку и начинает сначала на один символ позже, чем у него было предварительное сопоставление. На этот раз он идёт до следующего появления «foo». Полное регулярное выражение совпадает в этот раз, и вы получаете ожидаемый результат «table follows foo».

Иногда минимальное сопоставление может очень сильно помочь. Представьте, что вам нужно сопоставить всё между «foo» и «bar». Изначально вы пишите что-то вроде этого:

$_ =  "The food is under the bar in the barn.";
if ( /foo(.*)bar/ ) {
    print "got <$1>\n";
}

Что, возможно, неожиданно даёт:

got <d is under the bar in the >

Потому что .* был жадным, поэтому вы получаете всё между первой "foo" и последней "bar". Здесь эффективнее использовать минимальное соответствие, чтобы убедиться, что вы получаете текст между "foo" и первой последующей "bar".

  if ( /foo(.*?)bar/ ) { print "got <$1>\n" }
got <d is under the >

Вот ещё один пример. Предположим, вам нужно сопоставить число в конце строки, и вы также хотите сохранить предшествующую часть совпадения. Поэтому вы пишете это:

$_ = "I have 2 numbers: 53147";
if ( /(.*)(\d*)/ ) {                                # Wrong!
    print "Beginning is <$1>, number is <$2>.\n";
}

Это вообще не сработает, потому что .* был жадным и поглотил всю строку. Поскольку \d* может сопоставляться с пустой строкой, полное регулярное выражение успешно сопоставилось.

Beginning is <I have 2 numbers: 53147>, number is <>.

Вот несколько вариантов, большинство из которых не работают:

$_ = "I have 2 numbers: 53147";
@pats = qw{
    (.*)(\d*)
    (.*)(\d+)
    (.*?)(\d*)
    (.*?)(\d+)
    (.*)(\d+)$
    (.*?)(\d+)$
    (.*)\b(\d+)$
    (.*\D)(\d+)$
};

for $pat (@pats) {
    printf "%-12s ", $pat;
    if ( /$pat/ ) {
        print "<$1> <$2>\n";
    } else {
        print "FAIL\n";
    }
}

Это будет выводить:

(.*)(\d*)    <I have 2 numbers: 53147> <>
(.*)(\d+)    <I have 2 numbers: 5314> <7>
(.*?)(\d*)   <> <>
(.*?)(\d+)   <I have > <2>
(.*)(\d+)$   <I have 2 numbers: 5314> <7>
(.*?)(\d+)$  <I have 2 numbers: > <53147>
(.*)\b(\d+)$ <I have 2 numbers: > <53147>
(.*\D)(\d+)$ <I have 2 numbers: > <53147>

Как вы видите, это может быть немного сложно. Важно понимать, что регулярное выражение — это всего лишь набор утверждений, которые дают определение успешного совпадения. Может быть 0, 1 или несколько способов, которыми определение может быть успешно применено к конкретной строке. И если существует несколько способов, которыми это может быть выполнено, вам необходимо понять обратную отмотку, чтобы узнать, какой вариант успеха вы получите.

При использовании утверждений и отрицаний с опережающим просмотром всё может стать ещё сложнее. Представьте, что вы хотите найти последовательность нецифровых символов, за которой не следует "123". Вы можете попробовать написать это так

$_ = "ABC123";
if ( /^\D*(?!123)/ ) {                # Wrong!
    print "Yup, no 123 in $_\n";
}

Но это не будет соответствовать; по крайней мере, не так, как вы надеетесь. Он утверждает, что в строке нет "123". Вот более ясная картина того, почему этот шаблон соответствует, вопреки распространённому мнению:

$x = 'ABC123';
$y = 'ABC445';

print "1: got $1\n" if $x =~ /^(ABC)(?!123)/;
print "2: got $1\n" if $y =~ /^(ABC)(?!123)/;

print "3: got $1\n" if $x =~ /^(\D*)(?!123)/;
print "4: got $1\n" if $y =~ /^(\D*)(?!123)/;

Это выводит

2: got ABC
3: got AB
4: got ABC

Вы могли ожидать, что тест 3 провалится, потому что он кажется более универсальной версией теста 1. Важное различие между ними состоит в том, что тест 3 содержит квантификатор (\D*) и поэтому может использовать обратную отмотку, тогда как тест 1 не будет. Что происходит, так это то, что вы спросили: "Верно ли, что в начале $x, после 0 или более нецифровых символов, у вас есть что-то, что не является "123"? Если совпадение шаблонов позволило \D* расшириться до "ABC", это привело бы к провалу всего шаблона.

Поисковая система сначала сопоставит \D* со строкой "ABC". Затем она попытается сопоставить (?!123) со строкой "123", что не удастся. Но поскольку в регулярном выражении используется квантификатор (\D*), поисковая система может выполнить обратную отмотку и повторить совпадение по-другому в надежде на сопоставление всего регулярного выражения.

Шаблон действительно, действительно хочет добиться успеха, поэтому он использует стандартную обратную отмотку и позволяет \D* расшириться до "AB" на этот раз. Теперь действительно есть что-то, что следует за "AB", не являющееся "123". Это "C123", что достаточно.

Мы можем справиться с этим, используя и утверждение, и отрицание. Мы скажем, что первая часть в $1 должна следовать как за цифрой, так и за чем-то, что не является "123". Помните, что опережающие просмотры — выражения нулевой ширины — они только смотрят, но не потребляют никакую часть строки в своем соответствии. Поэтому переписывание таким образом даёт то, что вы ожидаете; то есть случай 5 провалится, а случай 6 будет успешным:

print "5: got $1\n" if $x =~ /^(\D*)(?=\d)(?!123)/;
print "6: got $1\n" if $y =~ /^(\D*)(?=\d)(?!123)/;

6: got ABC

Другими словами, два утверждения нулевой ширины, стоящие рядом, работают так, как будто они соединены операцией И, точно так же, как вы используете встроенные утверждения: /^$/ соответствует только тогда, когда вы находитесь в начале строки И в конце строки одновременно. Более глубокая истина заключается в том, что смежные элементы в регулярных выражениях всегда означают И, за исключением случаев, когда вы пишете явное ИЛИ с помощью вертикальной черты. /ab/ означает сопоставление "a" И (затем) сопоставление "b", хотя попытки сопоставлений выполняются в разных позициях, потому что "a" не утверждение нулевой ширины, а утверждение единичной ширины.

ПРЕДУПРЕЖДЕНИЕ: Особенно сложные регулярные выражения могут занимать экспоненциальное время для решения из-за огромного количества возможных способов, которыми они могут использовать обратную отмотку, чтобы попытаться найти соответствие. Например, без внутренних оптимизаций, выполняемых движком регулярных выражений, это займёт болезненно много времени для выполнения:

'aaaaaaaaaaaa' =~ /((a{0,5}){0,5})*[c]/

И если вы использовали "*" в внутренних группах вместо ограничения их до 0–5 совпадений, то это заняло бы вечность — или пока у вас не закончилось бы место в стеке. Более того, эти внутренние оптимизации не всегда применимы. Например, если вы поставите {0,5} вместо "*" во внешней группе, никакая текущая оптимизация не применима, и совпадение займёт много времени.

Мощным инструментом для оптимизации таких монстров является так называемая "независимая группа", которая не выполняет обратную отмотку (см. "(?>pattern)"). Обратите также внимание, что утверждения нулевой длины с опережающим и отстающим просмотром не будут выполнять обратную отмотку, чтобы выполнить совпадение хвоста, так как они находятся в "логическом" контексте: учитывается только то, совпадают ли они. Для примера, где побочные эффекты опережающего просмотра могут повлиять на последующее совпадение, см. "(?>pattern)".

Скриптовые последовательности

Скриптовая последовательность — это, по сути, последовательность символов, все из одного и того же Unicode-скрипта (см. "Скрипты" в perlunicode), такого как латинский или греческий. В большинстве случаев одно слово никогда не будет написано на нескольких скриптах, если это не атака подделки. Пример:

paypal.com

Эти буквы могут быть все латинскими (как в примере выше), или все кириллическими (кроме точки), или смесью тех и других. В случае интернет-адреса .com будет на латинице, а любые кириллические символы приведут к тому, что это будет смесь, а не скриптовая последовательность. Кто-то, кликнувший по такой ссылке, не попадет на настоящий веб-сайт PayPal, но злоумышленник создаст подделку, чтобы попытаться получить конфиденциальную информацию от этого человека.

Начиная с Perl 5.28, теперь легко обнаруживать строки, которые не являются скриптовыми последовательностями. Просто заключите практически любой шаблон, например, один из этих:

(*script_run:pattern)
(*sr:pattern)

Что происходит, так это то, что после того, как шаблон успешно сопоставлен, к нему применяется дополнительное условие, что каждый символ в нём должен быть из одного и того же скрипта (см. исключения ниже). Если это не так, происходит обратная отмотка, пока не будет найдено что-то, что целиком находится в одном и том же скрипте, или пока не будут исчерпаны все возможности. Это может привести к многочисленным обратным отмоткам, но обычно только вредоносный ввод приведёт к этому, хотя замедление может вызвать атаку типа "отказ в обслуживании". Если ваши потребности позволяют, лучше сделать шаблон атомарным, чтобы уменьшить количество обратных отмоток. Это, скорее всего, то, что вы хотите, поэтому вместо этого:

(*script_run:(?>pattern))

вы можете написать так:

(*atomic_script_run:pattern)
(*asr:pattern)

(См. "(?>pattern)".)

В Тайване, Японии и Корее часто текст содержит смесь символов из своих родных скриптов и базового китайского. Perl следует механизмам безопасности Unicode UTS 39 (https://unicode.org/reports/tr39/), позволяя такие смеси. Например, японские скрипты катакана и хирагана обычно смешиваются в практике, наряду с некоторыми китайскими символами, и поэтому рассматриваются Perl как принадлежащие к одной скриптовой последовательности.

Правила, используемые для сопоставления десятичных цифр, немного строже. Многие скрипты имеют свои наборы цифр, эквивалентные западным 0 через 9. У некоторых, таких как арабский, таких наборов больше одного. Чтобы строка считалась скриптовой последовательностью, все цифры в ней должны принадлежать одному и тому же набору из десяти, как определяется первой цифрой. Например,

qr/(*script_run: \d+ \b )/x

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

Unicode имеет три псевдо-скрипта, которые обрабатываются специально.

"Неизвестный" применяется к кодовым точкам, значение которых ещё не определено. В настоящее время Perl будет сопоставлять как скриптовые последовательности любые строковые последовательности длиной 1, состоящие из одного из этих кодовых точек. Но любые строки длиной более одного кодового точка, содержащие один из этих кодовых точек, не будут считаться скриптовыми последовательностями.

"Наследуемый" применяется к символам, которые изменяют другой, например, к акценту какого-либо типа. Они считаются частью скрипта основного символа и поэтому никогда не приводят к тому, что скриптовая последовательность не совпадает.

Другой — "Общий". Он включает в основном знаки препинания, эмодзи и символы, используемые в математике и музыке, ASCII-цифры 0 через 9, и полные формы этих цифр. Эти символы могут встречаться в тексте, смешанном со многими скриптами мира. Они также не препятствуют совпадению скриптовой последовательности. Но, как и в других скриптах, все цифры в последовательности должны принадлежать одному и тому же набору из десяти.

Этот конструктор не сохраняющий. Вы можете добавить скобки к шаблону, чтобы сохранить, если это необходимо. Вам придётся это сделать, если вы планируете использовать "(*ACCEPT) (*ACCEPT:arg)" и не хотите, чтобы он игнорировал проверку скриптовой последовательности.

Свойство Script_Extensions в модифицированной версии UTS 39 (https://unicode.org/reports/tr39/) используется в качестве основы для этой функции.

Резюме:

  • Все последовательности длины 0 или 1 являются скриптовыми последовательностями.

  • Более длинная последовательность является скриптовой последовательностью только в том случае, если все следующие условия соблюдаются:

    1. Ни одна кодовая точка в последовательности не имеет свойства Script_Extension Unknown.

      В настоящее время это означает, что все кодовые точки в последовательности были назначены Unicode как символы, которые не являются символами частного использования и не являются суррогатными кодовыми точками.

    2. Все символы в последовательности берутся из скрипта «Общий» и/или скрипта «Наследуемый» и/или одного другого скрипта.

      Скрипт символа определяется свойством Script_Extensions, модифицированным UTS 39 (https://unicode.org/reports/tr39/), как описано выше.

    3. Все десятичные цифры в последовательности берутся из одного и того же блока из десяти последовательных цифр.

Специальные команды управления обратной отмоткой

Эти специальные шаблоны, как правило, имеют вид (*VERB:arg). Если не указано иное, аргумент arg является необязательным; в некоторых случаях он обязателен.

Любой шаблон, содержащий специальную команду управления обратной отмоткой, которая допускает аргумент, имеет специальное поведение: при выполнении он устанавливает переменные текущего пакета $REGERROR и $REGMARK. При этом применяются следующие правила:

При ошибке переменная $REGERROR будет установлена в значение arg команды, если команда была вовлечена в ошибку соответствия. Если часть arg шаблона опущена, то $REGERROR будет установлена в имя последнего исполненного шаблона (*MARK:NAME), или в TRUE, если таковых не было. Также переменная $REGMARK будет установлена в FALSE.

При успешном совпадении переменная $REGERROR будет установлена в FALSE, а переменная $REGMARK — в имя последнего исполненного шаблона (*MARK:NAME). Подробные сведения см. в объяснении команды (*MARK:NAME) ниже.

ПРИМЕЧАНИЕ: $REGERROR и $REGMARK не являются магическими переменными, как $1 и большинство других переменных, связанных с регулярными выражениями. Они не локальны для области видимости, ни неизменяемы, а вместо этого являются переменными пакета, схожими с $AUTOLOAD. Они устанавливаются в пакете, содержащем код, который выполнил регулярное выражение (а не тот, который его скомпилировал, если они отличаются). При необходимости, можно использовать local для локализации изменений в этих переменных в определенной области видимости перед выполнением регулярного выражения.

Если шаблон не содержит специальный глагол обратного поиска, который позволяет аргумент, то $REGERROR и $REGMARK вообще не затрагиваются.

Глаголы
(*PRUNE) (*PRUNE:NAME)

Этот шаблон нулевой ширины обрезает дерево обратного поиска в текущей точке при обратном поиске при неудаче. Рассмотрим шаблон /A (*PRUNE) B/, где A и B — сложные шаблоны. До тех пор, пока не достигнут глагол (*PRUNE), A может выполнять обратный поиск по необходимости для соответствия. После достижения этого глагола, соответствие продолжается в B, которое также может выполнять обратный поиск по необходимости; однако, если B не соответствует, дальнейший обратный поиск не выполняется, и шаблон терпит неудачу сразу в текущей начальной позиции.

Следующий пример подсчитывает все возможные соответствия строк в шаблоне (без фактического соответствия ни одной из них).

'aaab' =~ /a+b?(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

что приводит к:

aaab
aaa
aa
a
aab
aa
a
ab
a
Count=9

Если мы добавим глагол (*PRUNE) перед подсчетом, как в следующем примере

'aaab' =~ /a+b?(*PRUNE)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

мы предотвращаем обратный поиск и находим количество самой длинной совпадающей строки в каждой точке начала соответствия, как показано ниже:

aaab
aab
ab
Count=3

Любое количество утверждений (*PRUNE) может быть использовано в шаблоне.

См. также "(?>pattern)" и поглощающие квантификаторы для других способов управления обратным поиском. В некоторых случаях использование (*PRUNE) может быть заменено на (?>pattern) без функциональных различий; однако, (*PRUNE) может использоваться для обработки случаев, которые не могут быть выражены только с помощью (?>pattern).

(*SKIP) (*SKIP:NAME)

Этот шаблон нулевой ширины аналогичен (*PRUNE), за исключением того, что при неудаче он также означает, что любой текст, который был сопоставлен до выполнения шаблона (*SKIP), не может быть частью любого соответствия этого шаблона. Это эффективно означает, что движок регулярных выражений «пропускает» вперёд до этой позиции при неудаче и пытается сопоставить снова (при условии, что достаточно места для соответствия).

Название шаблона (*SKIP:NAME) имеет особое значение. Если был встречен (*MARK:NAME), то используется именно эта позиция в качестве «точки пропуска». Если не встречен (*MARK) с таким именем, то оператор (*SKIP) не оказывает никакого влияния. При использовании без имени «точка пропуска» находится в месте, где находилась точка соответствия при выполнении шаблона (*SKIP).

Сравните следующее с примерами в (*PRUNE); обратите внимание, что строка вдвое длиннее:

'aaabaaab' =~ /a+b?(*SKIP)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

вывод

aaab
aaab
Count=2

После того, как «aaab» в начале строки совпало, и был выполнен (*SKIP), следующей начальной точкой будет позиция курсора, когда был выполнен (*SKIP).

(*MARK:NAME) (*:NAME)

Этот шаблон нулевой ширины может использоваться для маркировки точки, достигнутой в строке, когда определенная часть шаблона успешно сопоставлена. Эту метку можно назвать. Позже шаблон (*SKIP) пропустит вперёд до этой точки, если при обратном поиске возникнет неудача. Разрешается любое количество шаблонов (*MARK), и часть NAME может быть дублирована.

В дополнение к взаимодействию с шаблоном (*SKIP), (*MARK:NAME) может использоваться для «разметки» ветви шаблона, чтобы после соответствия программа могла определить, какие ветви шаблона участвовали в соответствии.

Когда соответствие успешно, переменная $REGMARK будет установлена на имя последнего выполненного (*MARK:NAME), который участвовал в соответствии.

Это можно использовать для определения, какая ветвь шаблона была сопоставлена, не используя отдельные группы захвата для каждой ветви, что, в свою очередь, может привести к повышению производительности, так как Perl не может оптимизировать /(?:(x)|(y)|(z))/ так эффективно, как что-то вроде /(?:x(*MARK:x)|y(*MARK:y)|z(*MARK:z))/.

Когда соответствие потерпело неудачу, и если другой глагол не участвовал в неудачном соответствии и не предоставил своё имя для использования, переменная $REGERROR будет установлена на имя последнего выполненного (*MARK:NAME).

См. «(*SKIP)» для более подробной информации.

В качестве сокращения (*MARK:NAME) может быть записано как (*:NAME).

(*THEN) (*THEN:NAME)

Это аналогично оператору «группа среза» :: из Raku. Как и (*PRUNE), этот глагол всегда соответствует, и при обратном поиске при неудаче он заставляет движок регулярных выражений попробовать следующую альтернативу в самой внутренней группе (захвата или другом), имеющей альтернативы. Две ветви (?(condition)yes-pattern|no-pattern) не считаются альтернативами с точки зрения (*THEN).

Его имя происходит из наблюдения, что этот оператор в сочетании с оператором альтернатив ("|") может использоваться для создания по существу блока if/then/else на основе шаблонов:

( COND (*THEN) FOO | COND2 (*THEN) BAR | COND3 (*THEN) BAZ )

Обратите внимание, что если этот оператор используется и НЕ внутри альтернативы, то он действует точно как оператор (*PRUNE).

/ A (*PRUNE) B /

равно

/ A (*THEN) B /

но

/ ( A (*THEN) B | C ) /

не равно

/ ( A (*PRUNE) B | C ) /

поскольку после соответствия A, но неудачи на B, глагол (*THEN) выполнит обратный поиск и попробует C; но глагол (*PRUNE) просто потерпит неудачу.

(*COMMIT) (*COMMIT:arg)

Это «шаблон подтверждения» Raku <commit> или :::. Это шаблон нулевой ширины, аналогичный (*SKIP), за исключением того, что при обратном поиске при неудаче он приводит к прямому отказу соответствия. Больше попыток найти допустимое соответствие путём продвижения указателя начала не будет. Например,

'aaabaaab' =~ /a+b?(*COMMIT)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

вывод

aaab
Count=1

Другими словами, после входа в (*COMMIT), если шаблон не соответствует, движок регулярных выражений не будет пытаться искать соответствие на оставшейся части строки.

(*FAIL) (*F) (*FAIL:arg)

Этот шаблон ничего не сопоставляет и всегда терпит неудачу. Он может быть использован для принудительного выполнения обратного поиска. Он эквивалентен (?!), но легче читается. Фактически, (?!) оптимизируется в (*FAIL) внутри. Вы можете указать аргумент, так что если соответствие потерпит неудачу из-за этого указания FAIL, аргумент можно получить из $REGERROR.

Он, вероятно, полезен только в сочетании с (?{}) или (??{}).

(*ACCEPT) (*ACCEPT:arg)

Этот шаблон ничего не сопоставляет и вызывает окончание успешного сопоставления в точке, в которой был встречен шаблон (*ACCEPT), независимо от того, есть ли ещё что-то для сопоставления в строке. При вложенных шаблонах, например рекурсии, или в подшаблонах, динамически сгенерированных с помощью (??{}), завершается только самый внутренний шаблон.

Если (*ACCEPT) находится внутри групп захвата, то группы помечаются как завершенные в точке, в которой был встречен (*ACCEPT). Например:

'AB' =~ /(A (A|B(*ACCEPT)|C) D)(E)/x;

будет соответствовать, и $1 будет AB, а $2 будет "B", $3 не будет установлено. Если была сопоставлена другая ветвь во внутренних скобках, например, в строке 'ACDE', то "D" и "E" должны были бы совпасть тоже.

Вы можете указать аргумент, который будет доступен в переменной $REGMARK после завершения соответствия.

Предупреждение о \1 вместо $1

Некоторые люди слишком привыкают к написанию таких выражений:

$pattern =~ s/(\W)/\\\1/g;

Это унаследовано (для \1 до \9) для правой части подстановки, чтобы не шокировать поклонников sed, но это плохая привычка. Это потому, что в PerlThink правая часть s/// — это строка с двойными кавычками. \1 в обычной строке с двойными кавычками означает управляющий символ A. Обычное значение Unix для \1 используется для s///. Однако, если вы приучите себя к этому, у вас возникнут проблемы, если вы добавите модификатор /e.

s/(\d+)/ \1 + 1 /eg;            # causes warning under -w

Или если вы попытаетесь сделать

s/(\d+)/\1000/;

Вы не можете этого избежать, сказав \{1}000, в то время как вы можете это исправить с помощью ${1}000. Операция интерполяции не должна смешиваться с операцией сопоставления обратной ссылки. Конечно, они означают две разные вещи слева от s///.

Повторные шаблоны, сопоставляющие подстроку нулевой длины

ПРЕДУПРЕЖДЕНИЕ: Впереди сложный материал (и проза). Этот раздел нуждается в переработке.

Регулярные выражения предоставляют лаконичный и мощный язык программирования. Как и с большинством других инструментов, мощность сопровождается возможностью причинить вред.

Распространённое злоупотребление этой мощью происходит из-за возможности создавать бесконечные циклы с помощью регулярных выражений, с чем-то таким безобидным как:

'foo' =~ m{ ( o? )* }x;

o? соответствует в начале «foo», и так как позиция в строке не сдвигается соответствием, o? будет соответствовать снова и снова из-за квантификатора "*". Другой распространённый способ создания подобного цикла — использование модификатора цикла /g.

@matches = ( 'foo' =~ m{ o? }xg );

или

print "match: <$&>\n" while 'foo' =~ m{ o? }xg;

или цикл, подразумеваемый split().

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

@chars = split //, $string;           # // is not magic in split
($whitewashed = $string) =~ s/()/ /g; # parens avoid magic s// /

Таким образом, Perl допускает такие конструкции, принудительно прерывая бесконечный цикл. Правила для этого отличаются для циклов нижнего уровня, задаваемых жадными квантификаторами *+{}, и для циклов высшего уровня, таких как модификатор /g или оператор split().

Циклы нижнего уровня прерываются (то есть, цикл прерывается), когда Perl обнаруживает, что повторяющееся выражение соответствует подстроке нулевой длины. Таким образом

m{ (?: NON_ZERO_LENGTH | ZERO_LENGTH )* }x;

становится эквивалентным

m{ (?: NON_ZERO_LENGTH )* (?: ZERO_LENGTH )? }x;

Например, эта программа

#!perl -l
"aaaaab" =~ /
  (?:
     a                 # non-zero
     |                 # or
    (?{print "hello"}) # print hello whenever this
                       #    branch is tried
    (?=(b))            # zero-width assertion
  )*  # any number of times
 /x;
print $&;
print $1;

выводит

hello
aaaaa
b

Обратите внимание, что «hello» выводится только один раз, так как когда Perl видит, что шестая итерация внешнего цикла (?:)* соответствует строке нулевой длины, он останавливает "*".

Циклы более высокого уровня сохраняют дополнительное состояние между итерациями: была ли последняя совпадение нулевой длины. Чтобы прервать цикл, последующее совпадение после совпадения нулевой длины должно иметь ненулевую длину. Это ограничение взаимодействует с возвратом назад (см. «Возврат назад»), поэтому выбирается второе по оптимальности совпадение, если лучшее совпадение имеет нулевую длину.

Например:

$_ = 'bar';
s/\w??/<$&>/g;

приводит к <><b><><a><><r><>. В каждой позиции строки лучшим совпадением, заданным нежадным ??, является совпадение нулевой длины, а второе по оптимальности совпадение – то, что соответствует \w. Таким образом, совпадения нулевой длины чередуются с совпадениями длиной в один символ.

Аналогично, для повторяющихся m/()/g вторым по оптимальности совпадением является совпадение в позиции на один шаг дальше в строке.

Дополнительное состояние совпадения нулевой длины связано со строкой совпадения и сбрасывается при каждом присвоении pos(). Совпадения нулевой длины в конце предыдущего совпадения игнорируются во время split.

Комбинирование элементов регулярных выражений

Каждый из элементарных фрагментов регулярных выражений, описанных ранее (например, ab или \Z ), может соответствовать не более чем одной подстроке в заданной позиции входной строки. Однако в типичном регулярном выражении эти элементарные фрагменты объединяются в более сложные шаблоны с помощью операторов объединения ST, S|T, S* и т. д. (в этих примерах "S" и "T" являются регулярными подвыражениями).

Такие комбинации могут включать альтернативы, что приводит к проблеме выбора: если мы применяем регулярное выражение a|ab к "abc", будет ли это совпадать с подстрокой "a" или "ab"? Один из способов описать, какая подстрока фактически соответствует, – это понятие возврата назад (см. «Возврат назад»). Однако это описание слишком низкого уровня и заставляет вас думать в терминах конкретной реализации.

Другое описание начинается с понятий «лучше»/«хуже». Все подстроки, которые могут соответствовать данному регулярному выражению, можно отсортировать от «лучшего» совпадения к «худшему» совпадению, и выбирается «лучшее» совпадение. Это подменяет вопрос «что выбирается?» вопросом «какие совпадения лучше, а какие хуже?».

Опять же, для элементарных фрагментов такого вопроса нет, так как возможно не более одного совпадения в данной позиции. В этом разделе описывается понятие «лучше/хуже» для операторов объединения. В описании ниже "S" и "T" являются регулярными подвыражениями.

ST

Рассмотрим два возможных совпадения, AB и A'B', "A" и A' являются подстроками, которые могут соответствовать "S", "B" и B' являются подстроками, которые могут соответствовать "T".

Если "A" является лучшим совпадением для "S" , чем A', то AB является лучшим совпадением, чем A'B'.

Если "A" и A' совпадают, то AB является лучшим совпадением, чем AB' , если "B" является лучшим совпадением для "T" , чем B'.

S|T

Когда "S" может соответствовать, это лучшее совпадение, чем когда может соответствовать только "T".

Порядок двух совпадений для "S" такой же, как и для "S". Аналогично для двух совпадений для "T".

S{REPEAT_COUNT}

Совпадает с SSS...S (повторяется столько раз, сколько необходимо).

S{min,max}

Совпадает с S{max}|S{max-1}|...|S{min+1}|S{min}.

S{min,max}?

Совпадает с S{min}|S{min+1}|...|S{max-1}|S{max}.

S?, S*, S+

То же, что и S{0,1}, S{0,BIG_NUMBER}, S{1,BIG_NUMBER} соответственно.

S??, S*?, S+?

То же, что и S{0,1}?, S{0,BIG_NUMBER}?, S{1,BIG_NUMBER}? соответственно.

(?>S)

Совпадает с лучшим совпадением для "S" и только с ним.

(?=S), (?<=S)

Рассматривается только лучшее совпадение для "S". (Это важно только если "S" имеет группирующие скобки, и обратные ссылки используются где-то еще во всем регулярном выражении.)

(?!S), (?<!S)

Для этого оператора группирования нет необходимости описывать порядок, так как важно только то, может ли "S" соответствовать или нет.

(??{ EXPR }), (?PARNO)

Порядок такой же, как и для регулярного выражения, являющегося результатом EXPR, или шаблона, содержащегося в группе захвата PARNO.

(?(condition)yes-pattern|no-pattern)

Вспомним, что то, какое из yes-pattern или no-pattern фактически соответствует, уже определено. Порядок совпадений такой же, как и для выбранного подвыражения.

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

Создание пользовательских движков регулярных выражений

Начиная с Perl 5.10.0, можно создавать пользовательские движки регулярных выражений. Это не для слабонервных, так как они должны подключаться на уровне C. См. perlreapi для получения дополнительной информации.

В качестве альтернативы перегруженные константы (см. overload) предоставляют простой способ расширения функциональности движка регулярных выражений путем подстановки одного шаблона другим.

Предположим, что мы хотим включить новую последовательность экранирования регулярного выражения \Y| , которая соответствует границе между символами пробела и не символами пробела. Обратите внимание, что (?=\S)(?<!\S)|(?!\S)(?<=\S) точно соответствует этим позициям, поэтому мы хотим, чтобы каждый \Y| находился на месте более сложного варианта. Мы можем создать модуль customre для этого:

package customre;
use overload;

sub import {
  shift;
  die "No argument to customre::import allowed" if @_;
  overload::constant 'qr' => \&convert;
}

sub invalid { die "/$_[0]/: invalid escape '\\$_[1]'"}

# We must also take care of not escaping the legitimate \\Y|
# sequence, hence the presence of '\\' in the conversion rules.
my %rules = ( '\\' => '\\\\',
              'Y|' => qr/(?=\S)(?<!\S)|(?!\S)(?<=\S)/ );
sub convert {
  my $re = shift;
  $re =~ s{
            \\ ( \\ | Y . )
          }
          { $rules{$1} or invalid($re,$1) }sgex;
  return $re;
}

Теперь use customre включает новое экранирование в константных регулярных выражениях, т.е., в тех, которые не содержат переменных интерполяций во время выполнения. Как документировано в overload, это преобразование будет работать только над литеральными частями регулярных выражений. Для \Y|$re\Y| переменная часть этого регулярного выражения должна быть преобразована явно (но только если специальное значение \Y| должно быть включено внутри $re):

use customre;
$re = <>;
chomp $re;
$re = customre::convert $re;
/\Y|$re\Y|/;

Частота выполнения встраиваемого кода

Точные правила того, как часто (??{}) и (?{}) выполняются в шаблоне, не определены. В случае успешного совпадения можно предположить, что они будут действовать «как обычно» и выполняться слева направо в нужном количестве раз в приемлемом пути шаблона, как и любой другой меташаблон. То, как пути, не приводящие к совпадению, и ошибки соответствия влияют на количество раз выполнения шаблона, не определено конкретно и может варьироваться в зависимости от применяемых оптимизаций к шаблону и, вероятно, будет меняться от версии к версии.

Например, в

"aaabcdeeeee"=~/a(?{print "a"})b(?{print "b"})cde/;

точное количество раз, когда выводятся «a» или «b», не определено при ошибке, но можно предположить, что они будут напечатаны по меньшей мере один раз во время успешного совпадения, а также предположить, что если выводится «b», ей предшествует по меньшей мере одно «a».

В случае разветвленных конструкций, таких как следующие:

/a(b|(?{ print "a" }))c(?{ print "c" })/;

можно предположить, что вход «ac» выведет «ac», а «abc» – только «c».

Когда встраиваемый код квантифицируется, успешные совпадения вызывают код один раз для каждой совпадающей итерации квантификатора. Например:

"good" =~ /g(?:o(?{print "o"}))*d/;

выведет «o» два раза.

Поддержка PCRE/Python

Начиная с Perl 5.10.0, Perl поддерживает несколько расширений синтаксиса регулярных выражений Python/PCRE. Хотя программисты Perl рекомендуют использовать Perl-специфичный синтаксис, следующие варианты также принимаются:

(?P<NAME>pattern)

Определение именованной группы захвата. Эквивалентно (?<NAME>pattern).

(?P=NAME)

Обратная ссылка на именованную группу захвата. Эквивалентно \g{NAME}.

(?P>NAME)

Вызов подпрограммы именованной группе захвата. Эквивалентно (?&NAME).

ОШИБКИ

Существует ряд проблем, связанных с нечувствительным к регистру соответствием в правилах Юникода. См. "i" в «Модификаторы» выше.

Этот документ колеблется от труднопонятного до совершенно и абсолютно непроницаемого. Странный словесный поток, наполненный жаргоном, трудно понять в нескольких местах.

Этот документ нуждается в переработке, которая разделяет обучающее содержание от справочного.

См. также

Синтаксис шаблонов, используемых в Perl для сопоставления шаблонов, эволюционировал из шаблонов, предоставленных в программном обеспечении для сопоставления регулярных выражений Bell Labs Research Unix 8-й версии (версия 8). (Код фактически происходит (удаленно) от свободно распространяемой реализации Генри Спенсера этих процедур V8.)

perlrequick.

perlretut.

«Операторы экранирования для регулярных выражений» в perlop.

«Подробные сведения о разборе цитируемых конструкций» в perlop.

perlfaq6.

«pos» в perlfunc.

perllocale.

perlebcdic.

Мастерство регулярных выражений Джеффри Фридла, издательство O'Reilly and Associates.

© 1993–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.36.0/perlre

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API