Spec-Zone.ru › Perl 5.34

perlre

СОДЕРЖАНИЕ

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

ИМЯ

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

ОПИСАНИЕ

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

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

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

В версии v5.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}/

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

Другое применение последовательностей обратного слэша — указать символы, которые нельзя (или которые вы не хотите) записывать буквально. Подробное описание их приведено в "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.

Метасимволы

"Основные принципы" представили некоторые метасимволы. Этот раздел предоставляет все из них. Большинство из них имеют то же значение, что и в команде egrep.

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

           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 и "Подробности синтаксического анализа цитированных конструкций" в perlop. Модификаторы можно добавлять динамически; см. "Расширенные шаблоны" ниже.

m

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

s

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

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

i

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

Если правила сопоставления по локалям активны, карта регистров берется из текущей локали для кодовых точек меньше 255 и из правил Юникода для кодовых точек большего размера. Однако совпадения, которые пересекают границу правил Юникода/не-Юникода (ord 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 не сопоставляет несколько символов в группе символов в квадратных скобках, если символ, который им соответствует, не указан явно, и не сопоставляет их вообще, если группа символов инвертирована, что в противном случае может быть очень запутанно. См. "Классы символов в квадратных скобках" в perlrecharclass и "Отрицание" в perlrecharclass.

x и xx

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

p

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

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

a, d, l и u

Эти модификаторы, все новые в 5.14, влияют на правила набора символов (Юникод и т. д.), как описано ниже в "Модификаторы наборов символов".

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/// соответственно).

Флаги, описанные далее в "Использование регулярных выражений в 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]/
/[!@"#$%^&*()=?<>']/

В совокупности эти функции значительно повышают удобочитаемость регулярных выражений 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{...}, могут иметь пробелы внутри и рядом с фигурными скобками, но не где-либо еще, и никаких символов пробела, отличных от пробелов. Исключение составляют свойства Юникода, которые следуют правилам Юникода, о которых см. "Свойства, доступные через \p{} и \P{}" в perluniprops.

Множество символов, которые считаются пробелами, являются теми, которые Юникод называет «Пробелами шаблона», а именно:

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, не беспокоиться о Юникоде.

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

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

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

/d — это старое, проблемное поведение 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

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

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

/l

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

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

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

В языках UTF-8 в версиях 5.20 и выше единственное заметное различие между языком и без него в регулярных выражениях должно быть связано с загрязнением (см. 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.

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

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

/d

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

  1. целевая строка закодирована в UTF-8;

  2. шаблон закодирован в UTF-8;

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

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

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

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

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

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

Другой мнемоникой для этого модификатора является «Зависит», так как используемые правила фактически зависят от различных факторов, а в результате вы можете получить неожиданные результаты. См. "Ошибка Unicode" в perlunicode. Ошибка Unicode стала довольно известной, что привело к еще одним (без ругательств) названиям для этого модификатора: «Сомнительный» и «Рискованный».

Если шаблон или строка не закодированы в UTF-8, то только символы ASCII могут положительно совпадать.

Вот несколько примеров того, как это работает на платформе ASCII:

$str =  "\xDF";      # $str is not in UTF-8 format.
$str =~ /^\w/;       # No match, as $str isn't in UTF-8 format.
$str .= "\x{0e0b}";  # Now $str is in UTF-8 format.
$str =~ /^\w/;       # Match! $str is now in UTF-8 format.
chop $str;
$str =~ /^\w/;       # Still a match! $str remains in UTF-8 format.

Этот модификатор автоматически выбирается по умолчанию, когда не выбраны другие, поэтому еще одно его название — «Стандартный».

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

/a (и /aa)

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

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

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

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

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

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

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

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

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

Какой модификатор набора символов используется по умолчанию?

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

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

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

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

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

До версии 5.14 не было явных модификаторов, но /l подразумевался для регулярных выражений, скомпилированных в пределах области действия use locale, и /d подразумевался в противном случае. Однако интерполяция регулярного выражения в более крупное регулярное выражение игнорирует исходную компиляцию в пользу того, что действовало во время второй компиляции. Существовало множество несоответствий (ошибок) с модификатором /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 предоставляет также форму «поssessive» квантификатора.

*+     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)" для получения более подробной информации; possessive квантификаторы - это просто синтаксический сахар для этого конструктора. Например, вышеупомянутый пример также может быть записан следующим образом:

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

Обратите внимание, что possessive модификатор квантификатора не может быть объединён с модификатором «нежадности». Это потому, что это не имело бы смысла. Рассмотрим следующую таблицу эквивалентности:

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

Последовательности экранирования

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

\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.

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

Кроме того, 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]

См. "Capture groups" ниже для получения подробностей.

[6]

См. "Extended Patterns" ниже для получения подробностей.

[7]

Обратите внимание, что \N имеет два значения. Когда он имеет вид \N{NAME}, он соответствует символу или последовательности символов с именем NAME; аналогично, когда он имеет вид \N{U+hex}, он соответствует символу с кодом Юникода 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. Обратите внимание, что правило для соответствий нулевой длины (см. "Repeated Patterns Matching a Zero-length Substring") несколько изменяется, так как содержимое слева от \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'.) name не должно начинаться с цифры и не должно содержать дефисов. Когда разные группы в одном шаблоне имеют одинаковое имя, любая ссылка на это имя предполагает самую левую определённую группу. Именованные группы учитываются в абсолютном и относительном нумерации, и поэтому также могут быть указаны этими номерами. (Можно выполнять действия с именованными группами захвата, которые в противном случае потребовали бы (??{}).)

Содержание группы захвата динамически ограничено и доступно вне шаблона до конца содержащего блока или до следующего успешного совпадения, в зависимости от того, что произойдет раньше. (См. "Составные операторы" в 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 это backspace). Perl разрешает эту неоднозначность, интерпретируя \10 как обратную ссылку только в том случае, если до неё было открыто как минимум 10 левых скобок. Аналогично, \2 — это обратная ссылка только в том случае, если до неё было открыто как минимум 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, и т.д) динамически ограничены до конца содержащего блока или до следующего успешного совпадения, в зависимости от того, что произойдёт раньше. (См. "Составные операторы" в perlsyn.)

ПРИМЕЧАНИЕ: Неуспешные совпадения в 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() или метаквотирующая последовательность escape \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" является особым в том, что его присутствие где-либо в шаблоне оказывает глобальное воздействие.

Отсутствие модификаторов делает это операцией бездействия (так зачем вы её указали, если это не сгенерированный код?), и начиная с версии 5.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 переменной длины, но при этом выводится предупреждение при компиляции шаблона, если она не выключена, в категории experimental::vlb. Это предупреждает вас о том, что точное поведение может измениться, если обратная связь от реального использования в поле укажет на это; или даже полное удаление, если обнаруженные проблемы не будут практически преодолимы. Вы можете добиться поведения, близкого к поведению до 5.30, запретив предупреждения в этой категории.

Существует специальная форма этого конструкта, называемая \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 переменной длины, но при этом выводится предупреждение при компиляции шаблона, если она не выключена, в категории experimental::vlb. Это предупреждает вас о том, что точное поведение может измениться, если обратная связь от реального использования в поле укажет на это; или даже полное удаление, если обнаруженные проблемы не будут практически преодолимы. Вы можете добиться поведения, близкого к поведению до 5.30, запретив предупреждения в этой категории.

Существует метод, который может быть использован для обработки 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', вы также должны включить проверку «загрязнения». Лучше всего использовать тщательно ограниченное вычисление в ячейке Safe. См. 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-pattern, если condition имеет истинное значение, и с no-pattern в противном случае. Отсутствие шаблона всегда соответствует.

(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-pattern никогда не выполняется напрямую, и no-pattern запрещено. Похоже по духу на (?{0}), но более эффективно. Подробнее см. ниже. Полный синтаксис: (?(DEFINE)definitions...)

Например:

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

соответствует куску нескобок, возможно, включенным в сами скобки.

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

Рекомендуется для этого использования поместить блок 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.

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

Обратная подстановка

ПРИМЕЧАНИЕ: Этот раздел представляет абстрактное приближение поведения регулярных выражений. Для более строгого (и сложного) представления правил, участвующих в выборе соответствия среди возможных альтернатив, см. "Объединение частей 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". В этот раз всё регулярное выражение соответствует, и вы получаете ожидаемый результат "таблица следует за 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)".

Бегущие скрипты

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

paypal.com

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

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

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

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

(*script_run:(?>pattern))

вы можете написать либо это:

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

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

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

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

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

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

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

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

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

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

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

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

Резюмируя,

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

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

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

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

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

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

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

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

Эти специальные шаблоны обычно имеют вид (*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).

ПОРОКИ

Существует ряд проблем, связанных с регистронезависимым соответствием в правилах Unicode. См. "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.34.0/perlre

Spec-Zone.ru

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