PHP 8.1.15 Released!

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

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

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

Обратная ссылка сопоставляется с частью строки, захваченной соответствующей подмаской, но не с самой подмаской. Таким образом шаблон (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
14
mnvx at yandex dot ru
6 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
3 months 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