CascadiaPHP 2024

Обратные ссылки

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

Однако, если число, которое следует за обратным слешем, меньше 10, оно всегда интерпретируется как обратная ссылка, и приводит к ошибке только тогда, когда нет соответствующего числа открывающих скобок. Другими словами, открывающие скобки не обязаны предшествовать ссылке для чисел меньше 10. «Упреждающая обратная ссылка» может иметь смысл, если используется повторение и более поздний подшаблон участвует в ранней итерации. Раздел «Экранирующие последовательности» даёт дополнительные сведения об обработке цифр, которые идут за обратным слешем.

Обратная ссылка сопоставляется с частью строки, которую захватил соответствующий подшаблон, но не с самим подшаблоном. Поэтому шаблон (sens|respons)e and \1ibility соответствует значениям «sense and sensibility» и «response and responsibility», но не «sense and responsibility». При сопоставлении обратной ссылки парсер также будет учитывать регистр, если обнаружит обратную ссылку во время регистрозависимого поиска. Например, шаблон ((?i)rah)\s+\1 соответствует значениям «rah rah» и «RAH RAH», но не «RAH rah», хотя сам подшаблон сопоставляется без учёта регистра.

На один и тот же подшаблон разрешено устанавливать несколько ссылок. Если подшаблон не участвовал в сопоставлении, то сопоставление со ссылкой на неё всегда терпит неудачу. Например, шаблон (a|(bc))\2 терпит неудачу, если находит соответствие с «a» раньше, чем с «bc». Поскольку может быть до 99 обратных ссылок, все цифры, следующие за обратным слешем, рассматриваются как часть потенциальной обратной ссылки. Если за ссылкой должна следовать цифра, необходимо использовать ограничитель. Если указан флаг PCRE_EXTENDED, ограничителем может быть любой пробельный символ. В противном случае можно использовать пустой комментарий.

Ссылка на подшаблон, внутри которого она расположена, терпит неудачу, если это первое сопоставление текущего подшаблона. Например, шаблон (a\1) не соответствует ни одной строке. Но всё же такие ссылки бывают полезны в повторяющихся подшаблонах. Например, шаблон (a|b\1)+ совпадает с любым количеством «a», «aba», «ababaa»... При каждой итерации подшаблона обратная ссылка соответствует той части строки, которая была захвачена при предыдущей итерации. Чтобы такая конструкция работала, шаблон должен быть построен так, чтобы при первой итерации сопоставление с обратной ссылкой не производилось. Этого можно достичь, используя альтернативы (как в предыдущем примере), либо квантификаторы с минимумом, равным нулю.

Управляющую последовательность \g указывают для абсолютных и относительных ссылок на подшаблоны. После этой последовательности должно быть указано беззнаковое или отрицательное число, при желании заключённое в фигурные скобки. Последовательности \1, \g1 и \g{1} эквивалентны друг другу. Использование этого шаблона с беззнаковым числом поможет избежать двусмысленности, присущей числам после обратного слеша. Это также помогает отличить обратные ссылки от символов в восьмеричном формате, а также упрощает запись числового литерала сразу после обратной ссылки, например, \g{2}1.

Использование отрицательных чисел с \g полезно при использовании относительных ссылок. Например, (foo)(bar)\g{-1} соответствует «foobarbar», а (foo)(bar)\g{-2} соответствует «foobarfoo». Это также может быть полезно в длинных шаблонах, как альтернатива отслеживания числа подшаблонов, на которые можно ссылаться в последующей части шаблона.

Обратную ссылку на именованный подшаблон указывают следующими записями: (?P=name), \k<name>, \k'name', \k{name}, \g{name}, \g<name> или \g'name'.

add a note

User Contributed Notes 2 notes

up
12
mnvx at yandex dot ru
8 years ago
Something similar opportunity is DEFINE.

Example:
(?(DEFINE)(?<myname>\bvery\b))(?&myname)\p{Pd}(?&myname).

Expression above will match "very-very" from next sentence:
Define is very-very handy sometimes.
^-------^

How it works. (?(DEFINE)(?<myname>\bvery\b)) - this block defines "myname" equal to "\bvery\b". So, this block "(?&myname)\p{Pd}(?&myname)" equvivalent to "\bvery\b\p{Pd}\bvery\b".
up
1
Steve
1 year ago
The escape sequence \g used as a backreference may not always behave as expected.
The following numbered backreferences refer to the text matching the specified capture group, as documented:
\1
\g1
\g{1}
\g-1
\g{-1}

However, the following variants refer to the subpattern code instead of the matched text:
\g<1>
\g'1'
\g<-1>
\g'-1'

With named backreferences, we may also use the \k escape sequence as well as the (?P=...) construct. The following combinations also refer to the text matching the named capture group, as documented:
\g{name}
\k{name}
\k<name>
\k'name'
(?P=name)

However, these refer to the subpattern code instead of the matched text:
g<name>
\g'name'

In the following example, the capture group searches for a single letter 'a' or 'b', and then the backreference looks for the same letter. Thus, the patterns are expected to match 'aa' and 'bb', but not 'ab' nor 'ba'.

<?php
/* Matches to the following patterns are replaced by 'xx' in the subject string 'aa ab ba bb'. */
$patterns = [
# numbered backreferences (absolute)
'/([ab])\1/', // 'xx ab ba xx'
'/([ab])\g1/', // 'xx ab ba xx'
'/([ab])\g{1}/', // 'xx ab ba xx'
'/([ab])\g<1>/', // 'xx xx xx xx' # unexpected behavior, backreference matches both 'a' and 'b'.
"/([ab])\g'1'/", // 'xx xx xx xx' # unexpected behavior, backreference matches both 'a' and 'b'.
'/([ab])\k{1}/', // 'aa ab ba bb' # No group with name "1", backreference to unset group always fails.
'/([ab])\k<1>/', // 'aa ab ba bb' # No group with name "1", backreference to unset group always fails.
"/([ab])\k'1'/", // 'aa ab ba bb' # No group with name "1", backreference to unset group always fails.
'/([ab])(?P=1)/', // NULL # Regex error: "subpattern name must start with a non-digit", (?P=) expects name not number.
# numbered backreferences (relative)
'/([ab])\-1/', // 'aa ab ba bb'
'/([ab])\g-1/', // 'xx ab ba xx'
'/([ab])\g{-1}/', // 'xx ab ba xx'
'/([ab])\g<-1>/', // 'xx xx xx xx' # unexpected behavior, backreference matches both 'a' and 'b'.
"/([ab])\g'-1'/", // 'xx xx xx xx' # unexpected behavior, backreference matches both 'a' and 'b'.
'/([ab])\k{-1}/', // 'aa ab ba bb' # No group with name "-1", backreference to unset group always fails.
'/([ab])\k<-1>/', // 'aa ab ba bb' # No group with name "-1", backreference to unset group always fails.
"/([ab])\k'-1'/", // 'aa ab ba bb' # No group with name "-1", backreference to unset group always fails.
'/([ab])(?P=-1)/', // NULL # Regex error: "subpattern name expected", (?P=) expects name not number.
# named backreferences
'/(?<name>[ab])\g{name}/', // 'xx ab ba xx'
'/(?<name>[ab])\g<name>/', // 'xx xx xx xx' # unexpected behavior, backreference matches both 'a' and 'b'.
"/(?<name>[ab])\g'name'/", // 'xx xx xx xx' # unexpected behavior, backreference matches both 'a' and 'b'.
'/(?<name>[ab])\k{name}/', // 'xx ab ba xx'
'/(?<name>[ab])\k<name>/', // 'xx ab ba xx'
"/(?<name>[ab])\k'name'/", // 'xx ab ba xx'
'/(?<name>[ab])(?P=name)/', // 'xx ab ba xx'
];

foreach (
$patterns as $pat)
echo
" '$pat',\t// " . var_export(@preg_replace($pat, 'xx', 'aa ab ba bb'), 1) . PHP_EOL;
?>
To Top