規則運算式中的反向參考建構

反向參考提供便利的方式來識別字串內的重複字元或子字串。 例如,如果輸入字串包含多次出現的任意子字串,您可以比對第一個出現的子字串與擷取的群組,接著使用反向參考來比對隨後出現的子字串。

注意

對於取代字串中的具名和編號擷取群組,會使用不同的語法來參考。 如需詳細資訊,請參閱替代

.NET 會定義個別的語言項目,以參考編號和具名擷取群組。 如需擷取群組的詳細資訊,請參閱群組建構

編號反向參考

編號反向參考會使用下列語法:

\number

其中 number 是規則運算式中的擷取群組序數位置。 例如,\4 會比對第四個擷取群組的內容。 如果規則運算式模式中未定義 number,便會發生剖析錯誤,而規則運算式引擎會擲回 ArgumentException。 例如,規則運算式 \b(\w+)\s\1 有效,因為 (\w+) 是運算式中第一個和唯一的擷取群組。 另一方面,\b(\w+)\s\2 無效並擲回引數例外狀況,因為沒有編號為 \2 的擷取群組。 此外,如果 number 識別在特定序數位置的擷取群組,但擷取群組已經被指派和其序數順序不同的數值名稱,則規則運算式剖析器也會擲回 ArgumentException

請注意八進位逸出字碼 (例如 \16) 與使用相同標記法之 \number 反向參考間的模稜兩可。 這個模棱兩可的情況已解決,如下所示:

  • 運算式 \1\9 一律會解譯為反向參考,而不是八進位字碼。

  • 如果多位數運算式的第一個數字是 8 或 9 (例如 \80\91),該運算式會解譯為常值。

  • \10 到更大值的運算式會視為反向參考 (如果有對應至該數字的反向參考),否則會解譯為八進位字碼。

  • 如果規則運算式包含未定義之群組號碼的反向參考,便會發生剖析錯誤,而規則運算式引擎會擲回 ArgumentException

如果模擬兩可會造成問題,您可以使用 \k<name> 標記法,這樣就不會造成模擬兩可,而且不會與八進位字元碼混淆。 同樣地,十六進位字碼 (例如 \xdd) 不會不明確,而且不會與反向參考混淆。

下列範例會在字串中尋找雙字組字元。 它會定義由下列項目組成的規則運算式 (\w)\1

元素 描述
(\w) 比對文字字元,並將其指派給第一個擷取群組。
\1 比對與第一個擷取群組之值相同的下一個字元。
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(\w)\1";
      string input = "trellis llama webbing dresser swagger";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine("Found '{0}' at position {1}.",
                           match.Value, match.Index);
   }
}
// The example displays the following output:
//       Found 'll' at position 3.
//       Found 'll' at position 8.
//       Found 'bb' at position 16.
//       Found 'ss' at position 25.
//       Found 'gg' at position 33.
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "(\w)\1"
        Dim input As String = "trellis llama webbing dresser swagger"
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("Found '{0}' at position {1}.", _
                              match.Value, match.Index)
        Next
    End Sub
End Module
' The example displays the following output:
'       Found 'll' at position 3.
'       Found 'll' at position 8.
'       Found 'bb' at position 16.
'       Found 'ss' at position 25.
'       Found 'gg' at position 33.

具名反向參考

具名反向參考是使用下列語法來定義:

\k<name>

or:

\k'name'

其中 name 是規則運算式模式中所定義之擷取群組的名稱。 如果規則運算式模式中未定義 name,便會發生剖析錯誤,而規則運算式引擎會擲回 ArgumentException

下列範例會在字串中尋找雙字組字元。 它會定義由下列項目組成的規則運算式 (?<char>\w)\k<char>

元素 描述
(?<char>\w) 比對字組字元,並將其指派給名為 char 的擷取群組。
\k<char> 比對下一個與 char 擷取群組值相同的字元。
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(?<char>\w)\k<char>";
      string input = "trellis llama webbing dresser swagger";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine("Found '{0}' at position {1}.",
                           match.Value, match.Index);
   }
}
// The example displays the following output:
//       Found 'll' at position 3.
//       Found 'll' at position 8.
//       Found 'bb' at position 16.
//       Found 'ss' at position 25.
//       Found 'gg' at position 33.
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "(?<char>\w)\k<char>"
        Dim input As String = "trellis llama webbing dresser swagger"
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("Found '{0}' at position {1}.", _
                              match.Value, match.Index)
        Next
    End Sub
End Module
' The example displays the following output:
'       Found 'll' at position 3.
'       Found 'll' at position 8.
'       Found 'bb' at position 16.
'       Found 'ss' at position 25.
'       Found 'gg' at position 33.

具名的數值反向參考

在含有 \k 的具名反向參考中,name 也可以是數字的字串表示。 例如,下列範例會使用規則運算式 (?<2>\w)\k<2> 來尋找字串中的雙字組字元。 在此案例中,範例定義了明確地命名為 "2" 的擷取群組,而反向參考也相對應地命名為 "2"。

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(?<2>\w)\k<2>";
      string input = "trellis llama webbing dresser swagger";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine("Found '{0}' at position {1}.",
                           match.Value, match.Index);
   }
}
// The example displays the following output:
//       Found 'll' at position 3.
//       Found 'll' at position 8.
//       Found 'bb' at position 16.
//       Found 'ss' at position 25.
//       Found 'gg' at position 33.
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "(?<2>\w)\k<2>"
        Dim input As String = "trellis llama webbing dresser swagger"
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("Found '{0}' at position {1}.", _
                              match.Value, match.Index)
        Next
    End Sub
End Module
' The example displays the following output:
'       Found 'll' at position 3.
'       Found 'll' at position 8.
'       Found 'bb' at position 16.
'       Found 'ss' at position 25.
'       Found 'gg' at position 33.

如果 name 是數值的字串表示,且沒有擷取群組具有該名稱,則 \k<name> 和反向參考 \number 是相同的,其中 number 是擷取的序數位置。 在下列範例中,有名為 char 的單一擷取群組。 反向參考建構將它參考為 \k<1>。 如範例的輸出所示,因為 char 是第一個擷取群組,因此對 Regex.IsMatch 的呼叫會成功。

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      Console.WriteLine(Regex.IsMatch("aa", @"(?<char>\w)\k<1>"));
      // Displays "True".
   }
}

Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Console.WriteLine(Regex.IsMatch("aa", "(?<char>\w)\k<1>"))
        ' Displays "True".
    End Sub
End Module

不過,如果 name 是數值的字串表示,且在該位置的擷取群組已經明確地被指派數值名稱,則規則運算式剖析器無法根據擷取群組的序數位置識別它。 相反地,會擲回 ArgumentException。 下列範例中的唯一擷取群組名為 "2"。 因為 \k 建構是用來定義名為 "1" 的反向參考,所以規則運算式剖析器無法識別第一個擷取群組並擲回例外狀況。

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      Console.WriteLine(Regex.IsMatch("aa", @"(?<2>\w)\k<1>"));
      // Throws an ArgumentException.
   }
}

Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Console.WriteLine(Regex.IsMatch("aa", "(?<2>\w)\k<1>"))
        ' Throws an ArgumentException.
    End Sub
End Module

反向參考比對的項目

反向參考會參考群組最近使用的定義 (由左至右比對時,最接近左邊的定義)。 當群組進行多個擷取時,反向參考會參考最近發生的擷取。

下列範例包含規則運算式模式 (?<1>a)(?<1>\1b)*,此模式可重新定義 \1 具名群組。 下表說明規則運算式中的每個模式。

模式 描述
(?<1>a) 比對字元 "a",並將結果指派給名為 1 的擷取群組。
(?<1>\1b)* 比對 0 或多次出現名為 1 和 "b" 之群組的情況,並將結果指派給名為 1 的擷取群組。
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(?<1>a)(?<1>\1b)*";
      string input = "aababb";
      foreach (Match match in Regex.Matches(input, pattern))
      {
         Console.WriteLine("Match: " + match.Value);
         foreach (Group group in match.Groups)
            Console.WriteLine("   Group: " + group.Value);
      }
   }
}
// The example displays the following output:
//          Group: aababb
//          Group: abb
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "(?<1>a)(?<1>\1b)*"
        Dim input As String = "aababb"
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("Match: " + match.Value)
            For Each group As Group In match.Groups
                Console.WriteLIne("   Group: " + group.Value)
            Next
        Next
    End Sub
End Module
' The example display the following output:
'          Group: aababb
'          Group: abb

在比較規則運算式與輸入字串 ("aababb") 時,規則運算式引擎會執行下列作業:

  1. 它會從該字串的開頭開始,並且成功地比對 "a" 與運算式 (?<1>a)1 群組的值現在是 "a"。

  2. 它會前進到第二個字元,並且成功地比對字串 "ab" 與運算式 \1b,或是 "ab"。 然後它會將結果 "ab" 指派給 \1

  3. 它會前進到第四個字元。 運算式 (?<1>\1b)* 要比對零次以上,才算成功地比對字串 "abb" 與運算式 \1b。 然後它會將結果 "abb" 指派回到 \1

在此範例中,* 是迴圈數量詞,它會重複評估直到規則運算式引擎無法符合其所定義的模式為止。 迴圈數量詞並不會清除群組定義。

如果群組沒有擷取任何子字串,該群組的反向參考會是未定義的,而且永遠不會進行比對。 這可由定義如下的規則運算式模式 \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b 來說明:

模式 描述
\b 開始字邊界比對。
(\p{Lu}{2}) 比對兩個大寫字母。 這是第一個擷取群組。
(\d{2})? 比對出現零次或一次的兩個十進位數字。 這是第二個擷取群組。
(\p{Lu}{2}) 比對兩個大寫字母。 這是第三個擷取群組。
\b 結束字邊界比對。

輸入字串可以符合這個規則運算式,即使不存在第二個擷取群組所定義的兩個十進位數字亦然。 下列範例顯示即使比對成功,仍會在兩個成功的擷取群組之間找到空的擷取群組。

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"\b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b";
      string[] inputs = { "AA22ZZ", "AABB" };
      foreach (string input in inputs)
      {
         Match match = Regex.Match(input, pattern);
         if (match.Success)
         {
            Console.WriteLine("Match in {0}: {1}", input, match.Value);
            if (match.Groups.Count > 1)
            {
               for (int ctr = 1; ctr <= match.Groups.Count - 1; ctr++)
               {
                  if (match.Groups[ctr].Success)
                     Console.WriteLine("Group {0}: {1}",
                                       ctr, match.Groups[ctr].Value);
                  else
                     Console.WriteLine("Group {0}: <no match>", ctr);
               }
            }
         }
         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//       Match in AA22ZZ: AA22ZZ
//       Group 1: AA
//       Group 2: 22
//       Group 3: ZZ
//
//       Match in AABB: AABB
//       Group 1: AA
//       Group 2: <no match>
//       Group 3: BB
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "\b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b"
        Dim inputs() As String = {"AA22ZZ", "AABB"}
        For Each input As String In inputs
            Dim match As Match = Regex.Match(input, pattern)
            If match.Success Then
                Console.WriteLine("Match in {0}: {1}", input, match.Value)
                If match.Groups.Count > 1 Then
                    For ctr As Integer = 1 To match.Groups.Count - 1
                        If match.Groups(ctr).Success Then
                            Console.WriteLine("Group {0}: {1}", _
                                              ctr, match.Groups(ctr).Value)
                        Else
                            Console.WriteLine("Group {0}: <no match>", ctr)
                        End If
                    Next
                End If
            End If
            Console.WriteLine()
        Next
    End Sub
End Module
' The example displays the following output:
'       Match in AA22ZZ: AA22ZZ
'       Group 1: AA
'       Group 2: 22
'       Group 3: ZZ
'       
'       Match in AABB: AABB
'       Group 1: AA
'       Group 2: <no match>
'       Group 3: BB

另請參閱