2024 PHP Conference Japan

反向引用

在字元類別之外,反斜線後接著大於 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 則註解

mnvx at yandex dot ru
8 年前
DEFINE 提供了類似的功能。

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

上述表達式將會從下個句子中比對 "very-very"
Define 有時非常方便。
^-------^

運作方式。(?(DEFINE)(?<myname>\bvery\b)) - 這個區塊定義了「myname」等於「\bvery\b」。因此,這個區塊「(&myname)\p{Pd}(&myname)」等同於「\bvery\b\p{Pd}\bvery\b」。
Steve
2 年前
作為反向參考使用的跳脫序列 \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
/* 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