反向引用

在字符类之外,反斜杠后跟大于 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"、"ababba"等。在子模式的每次迭代中,反向引用都匹配与上一次迭代相对应的字符字符串。为了使此方法起作用,模式必须是这样的,即第一次迭代不需要匹配反向引用。这可以通过使用交替来完成,如上面的示例中所示,或者通过具有零最小值的量词来完成。

\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'实现对命名子模式的反向引用。

添加注释

用户贡献的注释 2 个注释

12
mnvx at yandex dot ru
8 年前
DEFINE 提供了类似的机会。

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

上面的表达式将从下一句话中匹配 "very-very"
定义非常有用。
^-------^

工作原理。 (?(DEFINE)(?<myname>\bvery\b)) - 此块将 "myname" 定义为 "\bvery\b"。因此,此块 "(?&myname)\p{Pd}(?&myname)" 等效于 "\bvery\b\p{Pd}\bvery\b"。
1
Steve
1 年前
作为反向引用的转义序列 \g 可能不会始终如预期那样工作。
以下编号反向引用引用与指定捕获组匹配的文本,如文档中所述
\1
\g1
\g{1}
\g-1
\g{-1}

但是,以下变体引用子模式代码而不是匹配的文本
\g<1>
\g'1'
\g<-1>
\g'-1'

使用命名反向引用,我们还可以使用 \k 转义序列以及 (?P=...) 结构。以下组合也引用与命名捕获组匹配的文本,如文档中所述
\g{name}
\k{name}
\k<name>
\k'name'
(?P=name)

但是,这些引用子模式代码而不是匹配的文本
g<name>
\g'name'

在以下示例中,捕获组搜索单个字母 'a' 或 'b',然后反向引用查找相同的字母。因此,这些模式预计将匹配 'aa' 和 'bb',但不匹配 'ab' 或 'ba'。

<?php
/* 将匹配以下模式的字符串在目标字符串 'aa ab ba bb' 中替换为 'xx'。 */
$patterns = [
# 数字型反向引用(绝对)
'/([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' # 意外行为,反向引用匹配 'a' 和 'b'。
"/([ab])\g'1'/", // 'xx xx xx xx' # 意外行为,反向引用匹配 'a' 和 'b'。
'/([ab])\k{1}/', // 'aa ab ba bb' # 没有名为 "1" 的组,对未设置组的反向引用始终失败。
'/([ab])\k<1>/', // 'aa ab ba bb' # 没有名为 "1" 的组,对未设置组的反向引用始终失败。
"/([ab])\k'1'/", // 'aa ab ba bb' # 没有名为 "1" 的组,对未设置组的反向引用始终失败。
'/([ab])(?P=1)/', // NULL # 正则表达式错误: "子模式名称必须以非数字开头",(?P=) 需要名称而不是数字。
# 数字型反向引用(相对)
'/([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' # 意外行为,反向引用匹配 'a' 和 'b'。
"/([ab])\g'-1'/", // 'xx xx xx xx' # 意外行为,反向引用匹配 'a' 和 'b'。
'/([ab])\k{-1}/', // 'aa ab ba bb' # 没有名为 "-1" 的组,对未设置组的反向引用始终失败。
'/([ab])\k<-1>/', // 'aa ab ba bb' # 没有名为 "-1" 的组,对未设置组的反向引用始终失败。
"/([ab])\k'-1'/", // 'aa ab ba bb' # 没有名为 "-1" 的组,对未设置组的反向引用始终失败。
'/([ab])(?P=-1)/', // NULL # 正则表达式错误: "预期子模式名称",(?P=) 需要名称而不是数字。
# 命名型反向引用
'/(?<name>[ab])\g{name}/', // 'xx ab ba xx'
'/(?<name>[ab])\g<name>/', // 'xx xx xx xx' # 意外行为,反向引用匹配 'a' 和 'b'。
"/(?<name>[ab])\g'name'/", // 'xx xx xx xx' # 意外行为,反向引用匹配 'a' 和 '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