規則運算式選項

依預設,輸入字串與規則運算式模式中任何常值字元的比較會區分大小寫,規則運算式模式中的空白字元會解譯成常值空白字元,而規則運算式中的擷取群組會隱含也會明確命名。 您可以藉由指定規則運算式選項來修改這些預設的規則運算式行為及其其他幾個方面。 這其中的某些選項 (列示於下表) 可以內嵌為規則運算式模式的部分,或是提供給 System.Text.RegularExpressions.Regex 類別建構函式或靜態模式比對方法,以做為 System.Text.RegularExpressions.RegexOptions 列舉值。

RegexOptions 成員 內嵌字元 影響 其他相關資訊
None 不可用 使用預設行為。 預設選項
IgnoreCase i 使用不區分大小寫的比對方式。 不區分大小寫比對
Multiline m 使用多行模式,其中 ^$ 表示每一行的開頭與結尾 (而不是輸入字串的開頭和結尾)。 多行模式
Singleline s 使用單行模式,其中句點 (.) 會比對每個字元 (而不是 \n 以外的每個字元)。 單行模式
ExplicitCapture n 不擷取未命名的群組。 唯一有效的擷取是明確命名或編號的群組,格式如下:(?<name>subexpression) 僅明確擷取
Compiled 不可用 將規則運算式編譯為組件。 編譯的規則運算式
IgnorePatternWhitespace x 在模式中排除未逸出的空白字元,並且在數字符號 (#) 後面啟用註解。 忽略空白字元
RightToLeft 不可用 變更搜尋方向。 搜尋方向為由右至左,而不是由左至右。 由右至左模式
ECMAScript 不可用 設定運算式遵循 ECMAScript 標準的行為。 ECMAScript 匹配行為
CultureInvariant 不可用 忽略語言中的文化特性差異。 使用不變文化進行比較
NonBacktracking 不可用 使用可避免回溯的方法進行比對,並保證在輸入長度內進行線性時間處理。 (從 .NET 7 及更新版本開始提供。) 非回溯模式
AnyNewLine 不可用 ^$\Z.識別所有常見的換行序列,而不僅是 \n。 可用於 .NET 11 及更高版本。 AnyNewLine 模式

指定選項

您可以透過以下三種方式來指定規則運算式的選項:

  • options 類別建構函式或靜態(在 Visual Basic 中為 Shared)模式匹配方法的 System.Text.RegularExpressions.Regex 參數中,例如 Regex(String, RegexOptions)Regex.Match(String, String, RegexOptions)options 參數是 System.Text.RegularExpressions.RegexOptions 列舉值的位元「或」組合。

    當使用類別建構函式的 Regex 參數提供選項給 options 執行個體時,選項會指派給 System.Text.RegularExpressions.RegexOptions 屬性。 不過,System.Text.RegularExpressions.RegexOptions 屬性不會在正則表達式模式中反映內聯選項。

    下列範例提供一個實例。 其使用 options 方法的 Regex.Match(String, String, RegexOptions) 參數來啟用不區分大小寫比對,並且在識別以字母 "d" 開頭的文字時,忽略模式空白字元。

    string pattern = @"d \w+ \s";
    string input = "Dogs are decidedly good pets.";
    RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace;
    
    foreach (Match match in Regex.Matches(input, pattern, options))
        Console.WriteLine($"'{match.Value}// found at index {match.Index}.");
    // The example displays the following output:
    //    'Dogs // found at index 0.
    //    'decidedly // found at index 9.
    
    Dim pattern As String = "d \w+ \s"
    Dim input As String = "Dogs are decidedly good pets."
    Dim options As RegexOptions = RegexOptions.IgnoreCase Or RegexOptions.IgnorePatternWhitespace
    
    For Each match As Match In Regex.Matches(input, pattern, options)
        Console.WriteLine("'{0}' found at index {1}.", match.Value, match.Index)
    Next
    ' The example displays the following output:
    '    'Dogs ' found at index 0.
    '    'decidedly ' found at index 9.      
    
  • 使用語法 (?imnsx-imnsx),在正則表示式模式中套用內嵌選項。 此選項會從定義此選項的位置開始套用於模式,直到模式結尾,或是有其他內嵌選項取消此選項的定義為止。 請注意,System.Text.RegularExpressions.RegexOptions 執行個體的 Regex 屬性不會反映這些內嵌選項。 如需詳細資訊,請參閱其他建構主題。

    下列範例提供一個實例。 其使用內嵌選項來啟用不區分大小寫比對,並且在識別以字母 "d" 開頭的文字時,忽略模式空白字元。

    string pattern = @"(?ix) d \w+ \s";
    string input = "Dogs are decidedly good pets.";
    
    foreach (Match match in Regex.Matches(input, pattern))
        Console.WriteLine($"'{match.Value}// found at index {match.Index}.");
    // The example displays the following output:
    //    'Dogs // found at index 0.
    //    'decidedly // found at index 9.
    
    Dim pattern As String = "\b(?ix) d \w+ \s"
    Dim input As String = "Dogs are decidedly good pets."
    
    For Each match As Match In Regex.Matches(input, pattern)
        Console.WriteLine("'{0}' found at index {1}.", match.Value, match.Index)
    Next
    ' The example displays the following output:
    '    'Dogs ' found at index 0.
    '    'decidedly ' found at index 9.      
    
  • 在規則運算式模式中特定的群組建構中,使用語法 subexpression 套用內嵌選項。 如果一組選項前面沒有符號,將開啟這組選項;如果一組選項前面有減號,則將關閉這組選項。 (? 是語言建構語法的固定部分,無論啟用或停用選項,都需要此部分。)此選項僅適用於該群組。 如需詳細資訊,請參閱群組建構

    下列範例提供一個實例。 其使用群組建構中的內嵌選項來啟用不區分大小寫比對,並且在識別以字母 "d" 開頭的文字時,忽略模式空白字元。

    string pattern = @"\b(?ix: d \w+)\s";
    string input = "Dogs are decidedly good pets.";
    
    foreach (Match match in Regex.Matches(input, pattern))
        Console.WriteLine($"'{match.Value}// found at index {match.Index}.");
    // The example displays the following output:
    //    'Dogs // found at index 0.
    //    'decidedly // found at index 9.
    
    Dim pattern As String = "\b(?ix: d \w+)\s"
    Dim input As String = "Dogs are decidedly good pets."
    
    For Each match As Match In Regex.Matches(input, pattern)
        Console.WriteLine("'{0}' found at index {1}.", match.Value, match.Index)
    Next
    ' The example displays the following output:
    '    'Dogs ' found at index 0.
    '    'decidedly ' found at index 9.      
    

如果將選項指定為內嵌,選項或選項集前面的減號 (-) 會關閉那些選項。 例如,內嵌建構 (?ix-ms) 會開啟 RegexOptions.IgnoreCaseRegexOptions.IgnorePatternWhitespace 選項,並關閉 RegexOptions.MultilineRegexOptions.Singleline 選項。 依預設,會關所有正則表達式選項。

注意

如果建構函式或方法呼叫的 options 參數指定的規則運算式選項,與規則運算式模式中指定內嵌的選項相衝突,則會使用內嵌選項。

下列五個正則表示式選項可以透過選項參數和內嵌方式來設定:

以下七個正則表達式選項可用參數設定 options ,但無法內嵌設定:

判斷選項

您可以透過擷取唯讀 Regex 屬性的值來判定在 Regex.Options 物件實例化時提供了哪些選項。

若要測試任何選項 (RegexOptions.None 除外) 是否存在,請使用 Regex.Options 屬性的值和您感興趣的 RegexOptions 值來執行 AND 作業。 然後測試結果是否等於 RegexOptions 值。 下列範例會測試是否已設定 RegexOptions.IgnoreCase 選項。

if ((rgx.Options & RegexOptions.IgnoreCase) == RegexOptions.IgnoreCase)
    Console.WriteLine("Case-insensitive pattern comparison.");
else
    Console.WriteLine("Case-sensitive pattern comparison.");
If (rgx.Options And RegexOptions.IgnoreCase) = RegexOptions.IgnoreCase Then
    Console.WriteLine("Case-insensitive pattern comparison.")
Else
    Console.WriteLine("Case-sensitive pattern comparison.")
End If

若要測試 RegexOptions.None,請判定 Regex.Options 屬性的值是否等於 RegexOptions.None,如下列範例所示。

if (rgx.Options == RegexOptions.None)
    Console.WriteLine("No options have been set.");
If rgx.Options = RegexOptions.None Then
    Console.WriteLine("No options have been set.")
End If

以下章節列出 .NET 中正規表達式所支援的選項。

預設選項

RegexOptions.None 選項指出未指定任何選項,而規則運算式引擎使用其預設行為。 這包括下列項目:

  • 模式被解譯為標準規則運算式,而不是 ECMAScript 規則運算式。

  • 在輸入字串中,規則運算式模式由左至右匹配。

  • 比較會區分大小寫。

  • ^$ 語言項目表示輸入字串的開頭和結尾。 輸入字串的結尾可以是尾端新行 \n 字元。

  • . 語言項目會比對 \n 以外的每個字元。

  • 規則運算式模式中的任何空白字元會被解釋為文字空格字元。

  • 將模式與輸入字串比較時,會使用目前文化特性的慣例。

  • 規則運算式模式中的擷取群組是隱含的,也是明確的。

注意

RegexOptions.None 選項沒有內嵌等效選項。 將規則運算式選項套用為內嵌時,會關閉特定選項,依據各選項逐一還原預設行為。 例如,(?i) 會開啟不區分大小寫比較,而 (?-i) 會還原預設區分大小寫比較。

因為 RegexOptions.None 選項代表規則運算式引擎的預設行為,因此,在方法呼叫中很少被明確指定。 而是會呼叫不含 options 參數的建構函式或靜態模式比對方法。

不區分大小寫的比對

IgnoreCase 選項 (或 i 內嵌選項) 提供不區分大小寫比對。 依預設,會使用目前文化特性的大小寫慣例。

下列範例定義規則運算式模式 \bthe\w*\b,它會比對以 "the" 開頭的所有文字。 因為第一次呼叫 Match 方法時使用了預設的區分大小寫比較,所以輸出的結果顯示開頭的 "The" 字串沒有被比對到。 當使用選項設為 Match 來呼叫 IgnoreCase 方法時,將進行比對。

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"\bthe\w*\b";
      string input = "The man then told them about that event.";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine($"Found {match.Value} at index {match.Index}.");

      Console.WriteLine();
      foreach (Match match in Regex.Matches(input, pattern,
                                            RegexOptions.IgnoreCase))
         Console.WriteLine($"Found {match.Value} at index {match.Index}.");
   }
}
// The example displays the following output:
//       Found then at index 8.
//       Found them at index 18.
//
//       Found The at index 0.
//       Found then at index 8.
//       Found them at index 18.
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "\bthe\w*\b"
        Dim input As String = "The man then told them about that event."
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
        Next
        Console.WriteLine()
        For Each match As Match In Regex.Matches(input, pattern, _
                                                 RegexOptions.IgnoreCase)
            Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
        Next
    End Sub
End Module
' The example displays the following output:
'       Found then at index 8.
'       Found them at index 18.
'       
'       Found The at index 0.
'       Found then at index 8.
'       Found them at index 18.

下列範例會修改上一個範例中的規則運算式模式,改為使用內嵌選項,而不是使用 options 參數,以提供不區分大小寫比較。 群組建構中的第一個模式定義了不區分大小寫的選項,這個選項僅適用於字串 "the" 中的字母 "t"。 因為選項建構出現在模式開頭,所以第二個模式會將不區分大小寫選項套用於整個規則運算式。

using System;
using System.Text.RegularExpressions;

public class CaseExample
{
    public static void Main()
    {
        string pattern = @"\b(?i:t)he\w*\b";
        string input = "The man then told them about that event.";
        foreach (Match match in Regex.Matches(input, pattern))
            Console.WriteLine($"Found {match.Value} at index {match.Index}.");

        Console.WriteLine();
        pattern = @"(?i)\bthe\w*\b";
        foreach (Match match in Regex.Matches(input, pattern,
                                              RegexOptions.IgnoreCase))
            Console.WriteLine($"Found {match.Value} at index {match.Index}.");
    }
}
// The example displays the following output:
//       Found The at index 0.
//       Found then at index 8.
//       Found them at index 18.
//
//       Found The at index 0.
//       Found then at index 8.
//       Found them at index 18.
Imports System.Text.RegularExpressions

Module CaseExample
    Public Sub Main()
        Dim pattern As String = "\b(?i:t)he\w*\b"
        Dim input As String = "The man then told them about that event."
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
        Next
        Console.WriteLine()
        pattern = "(?i)\bthe\w*\b"
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
        Next
    End Sub
End Module

' The example displays the following output:
'       Found The at index 0.
'       Found then at index 8.
'       Found them at index 18.
'       
'       Found The at index 0.
'       Found then at index 8.
'       Found them at index 18.

多行模式

RegexOptions.Multiline 選項 (或 m 內嵌選項) 可讓規則運算式引擎處理構成多行的輸入字串。 它會變更 ^$ 語言項目的解譯,表示字行的開頭和結尾,而不是輸入字串的開頭和結尾。

根據預設,$ 只會在輸入字串的結尾滿足。 如果您指定 RegexOptions.Multiline 選項,則會由新行字元 (\n) 或輸入字串的結尾來滿足。

在這兩種情況下,$ 都不會辨識歸位字元/換行字元組合 (\r\n)。 $ 一律忽略任何回車符號 (\r)。 若要以 \r\n\n 結束比對,請使用子運算式 \r?$,而不只使用 $。 請注意,這會使 \r 成為匹配的一部分。

提示

從 .NET 11 開始,你可以使用 RegexOptions.AnyNewLine 來讓 ^$\Z. 能辨識所有常見的換行序列,而不只是 \n,這樣就不需要 \r? 的變通方法。 AnyNewLine 也被視為 \r\n 原子換行序列,因此 \r 從未包含在匹配中。 欲了解更多資訊,請參閱 AnyNewLine 模式 章節。

下列範例會擷取保齡球員的名字和分數,並將其加入 SortedList<TKey,TValue> 集合,以遞減順序排序。 呼叫 Matches 方法兩次。 在第一次呼叫方法時,規則運算式為 ^(\w+)\s(\d+)$,且沒有設定選項。 如輸出所示,因為規則運算式引擎無法隨著輸入字串的開頭和結尾來比對輸入模式,所以沒有找到相符項目。 在第二次呼叫方法時,規則運算式變更為 ^(\w+)\s(\d+)\r?$,且選項設為 RegexOptions.Multiline。 如輸出所示,已成功比對名字和分數,且分數以遞減順序顯示。

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class Multiline1Example
{
    public static void Main()
    {
        SortedList<int, string> scores = new SortedList<int, string>(new DescendingComparer1<int>());

        string input = "Joe 164\n" +
                       "Sam 208\n" +
                       "Allison 211\n" +
                       "Gwen 171\n";
        string pattern = @"^(\w+)\s(\d+)$";
        bool matched = false;

        Console.WriteLine("Without Multiline option:");
        foreach (Match match in Regex.Matches(input, pattern))
        {
            scores.Add(Int32.Parse(match.Groups[2].Value), (string)match.Groups[1].Value);
            matched = true;
        }
        if (!matched)
            Console.WriteLine("   No matches.");
        Console.WriteLine();

        // Redefine pattern to handle multiple lines.
        pattern = @"^(\w+)\s(\d+)\r*$";
        Console.WriteLine("With multiline option:");
        foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Multiline))
            scores.Add(Int32.Parse(match.Groups[2].Value), (string)match.Groups[1].Value);

        // List scores in descending order.
        foreach (KeyValuePair<int, string> score in scores)
            Console.WriteLine($"{score.Value}: {score.Key}");
    }
}

public class DescendingComparer1<T> : IComparer<T>
{
    public int Compare(T x, T y)
    {
        return Comparer<T>.Default.Compare(x, y) * -1;
    }
}
// The example displays the following output:
//   Without Multiline option:
//      No matches.
//
//   With multiline option:
//   Allison: 211
//   Sam: 208
//   Gwen: 171
//   Joe: 164
Imports System.Collections.Generic
Imports System.Text.RegularExpressions

Module Multiline1Example
    Public Sub Main()
        Dim scores As New SortedList(Of Integer, String)(New DescendingComparer1(Of Integer)())

        Dim input As String = "Joe 164" + vbCrLf +
                              "Sam 208" + vbCrLf +
                              "Allison 211" + vbCrLf +
                              "Gwen 171" + vbCrLf
        Dim pattern As String = "^(\w+)\s(\d+)$"
        Dim matched As Boolean = False

        Console.WriteLine("Without Multiline option:")
        For Each match As Match In Regex.Matches(input, pattern)
            scores.Add(CInt(match.Groups(2).Value), match.Groups(1).Value)
            matched = True
        Next
        If Not matched Then Console.WriteLine("   No matches.")
        Console.WriteLine()

        ' Redefine pattern to handle multiple lines.
        pattern = "^(\w+)\s(\d+)\r*$"
        Console.WriteLine("With multiline option:")
        For Each match As Match In Regex.Matches(input, pattern, RegexOptions.Multiline)
            scores.Add(CInt(match.Groups(2).Value), match.Groups(1).Value)
        Next
        ' List scores in descending order. 
        For Each score As KeyValuePair(Of Integer, String) In scores
            Console.WriteLine("{0}: {1}", score.Value, score.Key)
        Next
    End Sub
End Module

Public Class DescendingComparer1(Of T) : Implements IComparer(Of T)
    Public Function Compare(x As T, y As T) As Integer _
           Implements IComparer(Of T).Compare
        Return Comparer(Of T).Default.Compare(x, y) * -1
    End Function
End Class
' The example displays the following output:
'    Without Multiline option:
'       No matches.
'    
'    With multiline option:
'    Allison: 211
'    Sam: 208
'    Gwen: 171
'    Joe: 164

規則運算式模式 ^(\w+)\s(\d+)\r*$ 的定義如下表所示。

模式 描述
^ 從字行開頭開始。
(\w+) 比對一個或多個文字字元。 這是第一個擷取群組。
\s 比對空白字元。
(\d+) 比對一個或多個十進位數字。 這是第二個擷取群組。
\r? 比對零或一個回車字元。
$ 在字行結尾結束。

下列範例與上一個範例相同,只是下列範例是使用內嵌選項 (?m) 來設定多行選項。

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class Multiline2Example
{
    public static void Main()
    {
        SortedList<int, string> scores = new SortedList<int, string>(new DescendingComparer<int>());

        string input = "Joe 164\n" +
                       "Sam 208\n" +
                       "Allison 211\n" +
                       "Gwen 171\n";
        string pattern = @"(?m)^(\w+)\s(\d+)\r*$";

        foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Multiline))
            scores.Add(Convert.ToInt32(match.Groups[2].Value), match.Groups[1].Value);

        // List scores in descending order.
        foreach (KeyValuePair<int, string> score in scores)
            Console.WriteLine($"{score.Value}: {score.Key}");
    }
}

public class DescendingComparer<T> : IComparer<T>
{
    public int Compare(T x, T y)
    {
        return Comparer<T>.Default.Compare(x, y) * -1;
    }
}
// The example displays the following output:
//    Allison: 211
//    Sam: 208
//    Gwen: 171
//    Joe: 164
Imports System.Collections.Generic
Imports System.Text.RegularExpressions

Module Multiline2Example
    Public Sub Main()
        Dim scores As New SortedList(Of Integer, String)(New DescendingComparer(Of Integer)())

        Dim input As String = "Joe 164" + vbCrLf +
                              "Sam 208" + vbCrLf +
                              "Allison 211" + vbCrLf +
                              "Gwen 171" + vbCrLf
        Dim pattern As String = "(?m)^(\w+)\s(\d+)\r*$"

        For Each match As Match In Regex.Matches(input, pattern, RegexOptions.Multiline)
            scores.Add(CInt(match.Groups(2).Value), match.Groups(1).Value)
        Next
        ' List scores in descending order. 
        For Each score As KeyValuePair(Of Integer, String) In scores
            Console.WriteLine("{0}: {1}", score.Value, score.Key)
        Next
    End Sub
End Module

Public Class DescendingComparer(Of T) : Implements IComparer(Of T)
    Public Function Compare(x As T, y As T) As Integer _
           Implements IComparer(Of T).Compare
        Return Comparer(Of T).Default.Compare(x, y) * -1
    End Function
End Class
' The example displays the following output:
'    Allison: 211
'    Sam: 208
'    Gwen: 171
'    Joe: 164

單行模式

RegexOptions.Singleline 選項 (或 s 內嵌選項) 會使規則運算式引擎將輸入字串當作其包含單行。 其作法是變更句點 (.) 語言項目的行為,使其比對每個字元,而不是比對新行字元 \n 以外的每個字元。

下列範例說明,當您使用 . 選項時,RegexOptions.Singleline 語言項目的行為有何變更。 規則運算式 ^.+ 會從字串開頭開始,比對每一個字元。 根據預設,比對會在第一行結尾結束。正則表達式會比對回車字元 \r,但它不會比對 \n。 由於 RegexOptions.Singleline 選項會將整個輸入字串解譯為單行,因此它會比對輸入字串中的每個字元,包括 \n

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = "^.+";
      string input = "This is one line and" + Environment.NewLine + "this is the second.";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine(Regex.Escape(match.Value));

      Console.WriteLine();
      foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Singleline))
         Console.WriteLine(Regex.Escape(match.Value));
   }
}
// The example displays the following output:
//       This\ is\ one\ line\ and\r
//
//       This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "^.+"
        Dim input As String = "This is one line and" + vbCrLf + "this is the second."
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine(Regex.Escape(match.Value))
        Next
        Console.WriteLine()
        For Each match As Match In Regex.Matches(input, pattern, RegexOptions.SingleLine)
            Console.WriteLine(Regex.Escape(match.Value))
        Next
    End Sub
End Module
' The example displays the following output:
'       This\ is\ one\ line\ and\r
'       
'       This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.

下列範例與上一個範例相同,只是下列範例是使用內嵌選項 (?s) 來啟用單行模式。

using System;
using System.Text.RegularExpressions;

public class SingleLineExample
{
    public static void Main()
    {
        string pattern = "(?s)^.+";
        string input = "This is one line and" + Environment.NewLine + "this is the second.";

        foreach (Match match in Regex.Matches(input, pattern))
            Console.WriteLine(Regex.Escape(match.Value));
    }
}
// The example displays the following output:
//       This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.
Imports System.Text.RegularExpressions

Module SingleLineExample
    Public Sub Main()
        Dim pattern As String = "(?s)^.+"
        Dim input As String = "This is one line and" + vbCrLf + "this is the second."

        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine(Regex.Escape(match.Value))
        Next
    End Sub
End Module
' The example displays the following output:
'       This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.

僅明確擷取

依預設,擷取群組的定義方式是在規則運算式模式中使用括號。 具名群組是以 (?<name>subexpression) 語言選項來指派名稱或號碼,而未具名群組可透過索引來存取。 在 GroupCollection 物件中,未具名群組在具名群組之前。

群組建構通常只用來將數量詞套用至多個語言項目,我們對所擷取的子字串並不感興趣。 例如,如果下列正則表達式:

\b\(?((\w+),?\s?)+[\.!?]\)?

目的只是要從文件中擷取以句點、驚嘆號或問號結尾的句子,則我們只對所產生的句子 (由 Match 物件代表) 感興趣。 集合中的個別文字並不存在。

非後續使用的擷取群組可能會耗用很多資源,因為規則運算式引擎必須同時填入 GroupCollectionCaptureCollection 集合物件。 或者,您也可以使用 RegexOptions.ExplicitCapture 選項或 n 內嵌選項,指定唯一有效的擷取是 (?<name>subexpression) 建構所指定的明確命名或編號群組。

下列範例顯示了當呼叫 \b\(?((\w+),?\s?)+[\.!?]\)? 方法時,Match 規則運算式模式在有或沒有使用 RegexOptions.ExplicitCapture 選項的情況下所傳回的比對資訊。 當從第一次方法呼叫的輸出顯示時,正規表示式引擎會將擷取子字串的相關資訊完整填入 GroupCollectionCaptureCollection 集合物件中。 因為呼叫第二個方法時,options 設為 RegexOptions.ExplicitCapture,所以沒有擷取群組的資訊。

using System;
using System.Text.RegularExpressions;

public class Explicit1Example
{
    public static void Main()
    {
        string input = "This is the first sentence. Is it the beginning " +
                       "of a literary masterpiece? I think not. Instead, " +
                       "it is a nonsensical paragraph.";
        string pattern = @"\b\(?((?>\w+),?\s?)+[\.!?]\)?";
        Console.WriteLine("With implicit captures:");
        foreach (Match match in Regex.Matches(input, pattern))
        {
            Console.WriteLine($"The match: {match.Value}");
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine($"   Group {groupCtr}: {group.Value}");
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine($"      Capture {captureCtr}: {capture.Value}");
                    captureCtr++;
                }
            }
        }
        Console.WriteLine();
        Console.WriteLine("With explicit captures only:");
        foreach (Match match in Regex.Matches(input, pattern, RegexOptions.ExplicitCapture))
        {
            Console.WriteLine($"The match: {match.Value}");
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine($"   Group {groupCtr}: {group.Value}");
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine($"      Capture {captureCtr}: {capture.Value}");
                    captureCtr++;
                }
            }
        }
    }
}
// The example displays the following output:
//    With implicit captures:
//    The match: This is the first sentence.
//       Group 0: This is the first sentence.
//          Capture 0: This is the first sentence.
//       Group 1: sentence
//          Capture 0: This
//          Capture 1: is
//          Capture 2: the
//          Capture 3: first
//          Capture 4: sentence
//       Group 2: sentence
//          Capture 0: This
//          Capture 1: is
//          Capture 2: the
//          Capture 3: first
//          Capture 4: sentence
//    The match: Is it the beginning of a literary masterpiece?
//       Group 0: Is it the beginning of a literary masterpiece?
//          Capture 0: Is it the beginning of a literary masterpiece?
//       Group 1: masterpiece
//          Capture 0: Is
//          Capture 1: it
//          Capture 2: the
//          Capture 3: beginning
//          Capture 4: of
//          Capture 5: a
//          Capture 6: literary
//          Capture 7: masterpiece
//       Group 2: masterpiece
//          Capture 0: Is
//          Capture 1: it
//          Capture 2: the
//          Capture 3: beginning
//          Capture 4: of
//          Capture 5: a
//          Capture 6: literary
//          Capture 7: masterpiece
//    The match: I think not.
//       Group 0: I think not.
//          Capture 0: I think not.
//       Group 1: not
//          Capture 0: I
//          Capture 1: think
//          Capture 2: not
//       Group 2: not
//          Capture 0: I
//          Capture 1: think
//          Capture 2: not
//    The match: Instead, it is a nonsensical paragraph.
//       Group 0: Instead, it is a nonsensical paragraph.
//          Capture 0: Instead, it is a nonsensical paragraph.
//       Group 1: paragraph
//          Capture 0: Instead,
//          Capture 1: it
//          Capture 2: is
//          Capture 3: a
//          Capture 4: nonsensical
//          Capture 5: paragraph
//       Group 2: paragraph
//          Capture 0: Instead
//          Capture 1: it
//          Capture 2: is
//          Capture 3: a
//          Capture 4: nonsensical
//          Capture 5: paragraph
//
//    With explicit captures only:
//    The match: This is the first sentence.
//       Group 0: This is the first sentence.
//          Capture 0: This is the first sentence.
//    The match: Is it the beginning of a literary masterpiece?
//       Group 0: Is it the beginning of a literary masterpiece?
//          Capture 0: Is it the beginning of a literary masterpiece?
//    The match: I think not.
//       Group 0: I think not.
//          Capture 0: I think not.
//    The match: Instead, it is a nonsensical paragraph.
//       Group 0: Instead, it is a nonsensical paragraph.
//          Capture 0: Instead, it is a nonsensical paragraph.
Imports System.Text.RegularExpressions

Module Explicit1Example
    Public Sub Main()
        Dim input As String = "This is the first sentence. Is it the beginning " +
                              "of a literary masterpiece? I think not. Instead, " +
                              "it is a nonsensical paragraph."
        Dim pattern As String = "\b\(?((?>\w+),?\s?)+[\.!?]\)?"
        Console.WriteLine("With implicit captures:")
        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("The match: {0}", match.Value)
            Dim groupCtr As Integer = 0
            For Each group As Group In match.Groups
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value)
                groupCtr += 1
                Dim captureCtr As Integer = 0
                For Each capture As Capture In group.Captures
                    Console.WriteLine("      Capture {0}: {1}", captureCtr, capture.Value)
                    captureCtr += 1
                Next
            Next
        Next
        Console.WriteLine()
        Console.WriteLine("With explicit captures only:")
        For Each match As Match In Regex.Matches(input, pattern, RegexOptions.ExplicitCapture)
            Console.WriteLine("The match: {0}", match.Value)
            Dim groupCtr As Integer = 0
            For Each group As Group In match.Groups
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value)
                groupCtr += 1
                Dim captureCtr As Integer = 0
                For Each capture As Capture In group.Captures
                    Console.WriteLine("      Capture {0}: {1}", captureCtr, capture.Value)
                    captureCtr += 1
                Next
            Next
        Next
    End Sub
End Module
' The example displays the following output:
'    With implicit captures:
'    The match: This is the first sentence.
'       Group 0: This is the first sentence.
'          Capture 0: This is the first sentence.
'       Group 1: sentence
'          Capture 0: This
'          Capture 1: is
'          Capture 2: the
'          Capture 3: first
'          Capture 4: sentence
'       Group 2: sentence
'          Capture 0: This
'          Capture 1: is
'          Capture 2: the
'          Capture 3: first
'          Capture 4: sentence
'    The match: Is it the beginning of a literary masterpiece?
'       Group 0: Is it the beginning of a literary masterpiece?
'          Capture 0: Is it the beginning of a literary masterpiece?
'       Group 1: masterpiece
'          Capture 0: Is
'          Capture 1: it
'          Capture 2: the
'          Capture 3: beginning
'          Capture 4: of
'          Capture 5: a
'          Capture 6: literary
'          Capture 7: masterpiece
'       Group 2: masterpiece
'          Capture 0: Is
'          Capture 1: it
'          Capture 2: the
'          Capture 3: beginning
'          Capture 4: of
'          Capture 5: a
'          Capture 6: literary
'          Capture 7: masterpiece
'    The match: I think not.
'       Group 0: I think not.
'          Capture 0: I think not.
'       Group 1: not
'          Capture 0: I
'          Capture 1: think
'          Capture 2: not
'       Group 2: not
'          Capture 0: I
'          Capture 1: think
'          Capture 2: not
'    The match: Instead, it is a nonsensical paragraph.
'       Group 0: Instead, it is a nonsensical paragraph.
'          Capture 0: Instead, it is a nonsensical paragraph.
'       Group 1: paragraph
'          Capture 0: Instead,
'          Capture 1: it
'          Capture 2: is
'          Capture 3: a
'          Capture 4: nonsensical
'          Capture 5: paragraph
'       Group 2: paragraph
'          Capture 0: Instead
'          Capture 1: it
'          Capture 2: is
'          Capture 3: a
'          Capture 4: nonsensical
'          Capture 5: paragraph
'    
'    With explicit captures only:
'    The match: This is the first sentence.
'       Group 0: This is the first sentence.
'          Capture 0: This is the first sentence.
'    The match: Is it the beginning of a literary masterpiece?
'       Group 0: Is it the beginning of a literary masterpiece?
'          Capture 0: Is it the beginning of a literary masterpiece?
'    The match: I think not.
'       Group 0: I think not.
'          Capture 0: I think not.
'    The match: Instead, it is a nonsensical paragraph.
'       Group 0: Instead, it is a nonsensical paragraph.
'          Capture 0: Instead, it is a nonsensical paragraph.

規則運算式模式 \b\(?((?>\w+),?\s?)+[\.!?]\)? 的定義如下表所示。

模式 描述
\b 從字邊界開始。
\(? 匹配零或一次出現的左括號 ("(")。
(?>\w+),? 匹配一個或多個文字字元,後面可接零或一個逗號。 比對文字字元時,請勿回溯。
\s? 比對零個或一個空白字元。
((\w+),?\s?)+ 一或多次比對一或多個文字字元、零或一個逗號及零或一個空白字元的組合。
[\.!?]\)? 比對這三種標點符號中的任一種,後面接零或一個右括號 (")")。

您也可以使用 (?n) 內嵌元素來隱藏自動擷取。 下列範例會修改上一個規則運算式模式,以使用 (?n) 內嵌項目,而不是使用 RegexOptions.ExplicitCapture 選項。

using System;
using System.Text.RegularExpressions;

public class Explicit2Example
{
    public static void Main()
    {
        string input = "This is the first sentence. Is it the beginning " +
                       "of a literary masterpiece? I think not. Instead, " +
                       "it is a nonsensical paragraph.";
        string pattern = @"(?n)\b\(?((?>\w+),?\s?)+[\.!?]\)?";

        foreach (Match match in Regex.Matches(input, pattern))
        {
            Console.WriteLine($"The match: {match.Value}");
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine($"   Group {groupCtr}: {group.Value}");
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine($"      Capture {captureCtr}: {capture.Value}");
                    captureCtr++;
                }
            }
        }
    }
}
// The example displays the following output:
//       The match: This is the first sentence.
//          Group 0: This is the first sentence.
//             Capture 0: This is the first sentence.
//       The match: Is it the beginning of a literary masterpiece?
//          Group 0: Is it the beginning of a literary masterpiece?
//             Capture 0: Is it the beginning of a literary masterpiece?
//       The match: I think not.
//          Group 0: I think not.
//             Capture 0: I think not.
//       The match: Instead, it is a nonsensical paragraph.
//          Group 0: Instead, it is a nonsensical paragraph.
//             Capture 0: Instead, it is a nonsensical paragraph.
Imports System.Text.RegularExpressions

Module Explicit2Example
    Public Sub Main()
        Dim input As String = "This is the first sentence. Is it the beginning " +
                              "of a literary masterpiece? I think not. Instead, " +
                              "it is a nonsensical paragraph."
        Dim pattern As String = "(?n)\b\(?((?>\w+),?\s?)+[\.!?]\)?"

        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("The match: {0}", match.Value)
            Dim groupCtr As Integer = 0
            For Each group As Group In match.Groups
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value)
                groupCtr += 1
                Dim captureCtr As Integer = 0
                For Each capture As Capture In group.Captures
                    Console.WriteLine("      Capture {0}: {1}", captureCtr, capture.Value)
                    captureCtr += 1
                Next
            Next
        Next
    End Sub
End Module
' The example displays the following output:
'       The match: This is the first sentence.
'          Group 0: This is the first sentence.
'             Capture 0: This is the first sentence.
'       The match: Is it the beginning of a literary masterpiece?
'          Group 0: Is it the beginning of a literary masterpiece?
'             Capture 0: Is it the beginning of a literary masterpiece?
'       The match: I think not.
'          Group 0: I think not.
'             Capture 0: I think not.
'       The match: Instead, it is a nonsensical paragraph.
'          Group 0: Instead, it is a nonsensical paragraph.
'             Capture 0: Instead, it is a nonsensical paragraph.

最後,您可以使用內嵌群組項目 (?n:),針對每個群組逐一隱藏自動擷取。 下列範例會修改上一個模式,以隱藏外部群組 ((?>\w+),?\s?) 中的未具名擷取。 請注意,這也會隱藏內部群組中的未具名擷取。

using System;
using System.Text.RegularExpressions;

public class Explicit3Example
{
    public static void Main()
    {
        string input = "This is the first sentence. Is it the beginning " +
                       "of a literary masterpiece? I think not. Instead, " +
                       "it is a nonsensical paragraph.";
        string pattern = @"\b\(?(?n:(?>\w+),?\s?)+[\.!?]\)?";

        foreach (Match match in Regex.Matches(input, pattern))
        {
            Console.WriteLine($"The match: {match.Value}");
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine($"   Group {groupCtr}: {group.Value}");
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine($"      Capture {captureCtr}: {capture.Value}");
                    captureCtr++;
                }
            }
        }
    }
}
// The example displays the following output:
//       The match: This is the first sentence.
//          Group 0: This is the first sentence.
//             Capture 0: This is the first sentence.
//       The match: Is it the beginning of a literary masterpiece?
//          Group 0: Is it the beginning of a literary masterpiece?
//             Capture 0: Is it the beginning of a literary masterpiece?
//       The match: I think not.
//          Group 0: I think not.
//             Capture 0: I think not.
//       The match: Instead, it is a nonsensical paragraph.
//          Group 0: Instead, it is a nonsensical paragraph.
//             Capture 0: Instead, it is a nonsensical paragraph.
Imports System.Text.RegularExpressions

Module Explicit3Example
    Public Sub Main()
        Dim input As String = "This is the first sentence. Is it the beginning " +
                              "of a literary masterpiece? I think not. Instead, " +
                              "it is a nonsensical paragraph."
        Dim pattern As String = "\b\(?(?n:(?>\w+),?\s?)+[\.!?]\)?"

        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine("The match: {0}", match.Value)
            Dim groupCtr As Integer = 0
            For Each group As Group In match.Groups
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value)
                groupCtr += 1
                Dim captureCtr As Integer = 0
                For Each capture As Capture In group.Captures
                    Console.WriteLine("      Capture {0}: {1}", captureCtr, capture.Value)
                    captureCtr += 1
                Next
            Next
        Next
    End Sub
End Module
' The example displays the following output:
'       The match: This is the first sentence.
'          Group 0: This is the first sentence.
'             Capture 0: This is the first sentence.
'       The match: Is it the beginning of a literary masterpiece?
'          Group 0: Is it the beginning of a literary masterpiece?
'             Capture 0: Is it the beginning of a literary masterpiece?
'       The match: I think not.
'          Group 0: I think not.
'             Capture 0: I think not.
'       The match: Instead, it is a nonsensical paragraph.
'          Group 0: Instead, it is a nonsensical paragraph.
'             Capture 0: Instead, it is a nonsensical paragraph.

編譯的正則表達式

注意

可能的話,請使用來源產生的規則運算式,而不是使用 RegexOptions.Compiled 選項編譯規則運算式。 來源產生可協助您的應用程式更快啟動、執行得更快速且更易於修剪。 若要了解來源產生是否可行,請參閱使用時機

在 .NET 中,正則表達式在預設情況下會被解讀。 將 Regex 物件具現化,或是呼叫靜態 Regex 方法時,會將規則運算式模式剖析成一組自訂 opcode,而解譯器會使用這些 opcode 來執行規則運算式。 這涉及一個權衡:初始化正則表達式引擎的成本會以犧牲執行時效能為代價來最小化。

您可以使用 RegexOptions.Compiled 選項,以編譯的規則運算式來取代解譯的規則運算式。 在這個情況下,將模式傳遞至規則運算式時,會將該模式剖析成一組自訂作業碼,然後再轉換成通用中間語言 (CIL),可直接傳遞至通用語言執行平台。 編譯後的正規表達式能以初始化時間為代價,最大化執行時效能。

注意

若要編譯規則運算式,唯一的方法就是提供 RegexOptions.Compiled 值給 options 類別建構函式或靜態模式比對方法的 Regex 參數。 無法作為內嵌選項使用。

在呼叫靜態和實例正則表達式時,都可以使用編譯的正則表達式。 在靜態規則運算式中,會將 RegexOptions.Compiled 選項傳遞至規則運算式模式比對方法的 options 參數。 在實例正則運算式中,會將其傳遞到 options 類別構造函式的 Regex 參數。 在這兩個情況中,都會增強效能。

不過,只有在下列條件下,效能才會提升:

  • 在對規則運算式模式比對方法的多個呼叫中,都會使用代表特定規則運算式的 Regex 物件。

  • Regex 物件不能超出範圍,因此可以重複使用。

  • 在對規則運算式模式比對方法的多個呼叫中,會使用靜態規則運算式。 (效能提升是有可能的,因為規則運算式引擎會快取靜態方法呼叫中所使用的規則運算式。)

注意

RegexOptions.Compiled 選項與過時的 Regex.CompileToAssembly 方法無關,此方法會建立特殊用途的組件,其中包含預先定義的編譯規則運算式。

忽略空白字元

依預設,規則運算式模式中的空白字元很重要;它會強制規則運算式引擎比對輸入字串中的空白字元。 因此,規則運算式 "\b\w+\s" 和 "\b\w+" 是大致相等的規則運算式。 此外,在規則運算式模式中遇到數字符號 (#) 時,會將其解譯成常值字元,以供比對。

RegexOptions.IgnorePatternWhitespace 選項 (或 x 內嵌選項) 會變更此預設行為,如下所示:

  • 規則運算式模式中未逸出的空白字元會被忽略。 若要在規則運算式模式中使用空白字元,就必須將它逸出 (例如,\s 或 "\")。

  • 數字符號 (#) 會解譯成註解的開頭,而不是常值字元。 規則運算式模式中,從 # 字元到下一個 \n 字元或字串結尾的所有文字會被解譯成註解。

但在下列案例中,即使您使用 RegexOptions.IgnorePatternWhitespace 選項,也不會忽略規則運算式中的空白字元:

  • 字元類別中的空白字元一律按字面解釋。 例如,規則運算式模式 [ .,;:] 會比對任何單一空白字元、句點、逗號、分號或冒號。

  • 方括號數量詞中不允許空白字元,例如 {n}{n,}{n,m}。 例如,規則運算式模式 \d{1, 3} 無法比對從一到三位數的任何數字序列,因為其中包含空白字元。

  • 引進語言項目的字元序列中,不允許空白字元。 例如:

    • 語言元素 (?:subexpression) 代表非擷取群組,而該項目的 (?: 部分不能有內嵌空格。 模式 (? :子表達式) 在執行時拋出一個 ArgumentException,因為正則表達式引擎無法解析該模式,且模式 ( ?:子表達式) 無法匹配 子表達式

    • 語言元素 \p{name} 代表 Unicode 類別或具名資料區塊,不能在此元素的 \p{ 部分中包含內嵌空格。 如果你加入空白,元素會在執行時拋出 。ArgumentException

啟用此選項有助於簡化通常很難剖析及了解的規則運算式。 增進了可讀性,並且讓規則運算式可以被記錄下來。

下列範例定義下列規則運算式模式:

\b \(? ( (?>\w+) ,?\s? )+ [\.!?] \)? # Matches an entire sentence.

此模式類似僅明確擷取一節中定義的模式,只是其會使用 RegexOptions.IgnorePatternWhitespace 選項來忽略模式空白字元。

using System;
using System.Text.RegularExpressions;

public class Whitespace1Example
{
    public static void Main()
    {
        string input = "This is the first sentence. Is it the beginning " +
                       "of a literary masterpiece? I think not. Instead, " +
                       "it is a nonsensical paragraph.";
        string pattern = @"\b \(? ( (?>\w+) ,?\s? )+ [\.!?] \)? # Matches an entire sentence.";

        foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnorePatternWhitespace))
            Console.WriteLine(match.Value);
    }
}
// The example displays the following output:
//       This is the first sentence.
//       Is it the beginning of a literary masterpiece?
//       I think not.
//       Instead, it is a nonsensical paragraph.
Imports System.Text.RegularExpressions

Module Whitespace1Example
    Public Sub Main()
        Dim input As String = "This is the first sentence. Is it the beginning " +
                              "of a literary masterpiece? I think not. Instead, " +
                              "it is a nonsensical paragraph."
        Dim pattern As String = "\b \(? ( (?>\w+) ,?\s? )+  [\.!?] \)? # Matches an entire sentence."

        For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnorePatternWhitespace)
            Console.WriteLine(match.Value)
        Next
    End Sub
End Module
' The example displays the following output:
'       This is the first sentence.
'       Is it the beginning of a literary masterpiece?
'       I think not.
'       Instead, it is a nonsensical paragraph.

下列範例使用內嵌選項 (?x) 來忽略模式空白字元。

using System;
using System.Text.RegularExpressions;

public class Whitespace2Example
{
    public static void Main()
    {
        string input = "This is the first sentence. Is it the beginning " +
                       "of a literary masterpiece? I think not. Instead, " +
                       "it is a nonsensical paragraph.";
        string pattern = @"(?x)\b \(? ( (?>\w+) ,?\s? )+  [\.!?] \)? # Matches an entire sentence.";

        foreach (Match match in Regex.Matches(input, pattern))
            Console.WriteLine(match.Value);
    }
}
// The example displays the following output:
//       This is the first sentence.
//       Is it the beginning of a literary masterpiece?
//       I think not.
//       Instead, it is a nonsensical paragraph.
Imports System.Text.RegularExpressions

Module Whitespace2Example
    Public Sub Main()
        Dim input As String = "This is the first sentence. Is it the beginning " +
                              "of a literary masterpiece? I think not. Instead, " +
                              "it is a nonsensical paragraph."
        Dim pattern As String = "(?x)\b \(? ( (?>\w+) ,?\s? )+  [\.!?] \)? # Matches an entire sentence."

        For Each match As Match In Regex.Matches(input, pattern)
            Console.WriteLine(match.Value)
        Next
    End Sub
End Module
' The example displays the following output:
'       This is the first sentence.
'       Is it the beginning of a literary masterpiece?
'       I think not.
'       Instead, it is a nonsensical paragraph.

由右至左模式

依預設,規則運算式引擎會由左至右搜尋。 您可以使用 RegexOptions.RightToLeft 選項來反轉搜尋方向。 由右至左搜尋會自動從字串最後一個字元的位置開始。 針對包含開始位置參數的模式比對方法,例如 Regex.Match(String, Int32),指定的開始位置是從最右邊字元位置開始搜尋的索引。

注意

若要使用由右至左模式,唯一的方法就是提供 RegexOptions.RightToLeft 值給 options 類別建構函式或靜態模式比對方法的 Regex 參數。 無法作為內嵌選項供應。

範例

規則運算式 \bb\w+\s 會比對有兩個或多個以字母 "b" 開頭、後接空白字元之字元的文字。 在下列範例中,輸入字串是由包含一或數個 "b" 字元的三個單字所組成。 第一個和第二個字開頭為 "b",第三個字結尾為 "b"。 如從右至左搜尋範例的輸出所示,只有第一個和第二個詞符合正則表達式模式,且第二個詞先被比對。

using System;
using System.Text.RegularExpressions;

public class RTL1Example
{
    public static void Main()
    {
        string pattern = @"\bb\w+\s";
        string input = "build band tab";
        foreach (Match match in Regex.Matches(input, pattern, RegexOptions.RightToLeft))
            Console.WriteLine($"'{match.Value}' found at position {match.Index}.");
    }
}
// The example displays the following output:
//       'band ' found at position 6.
//       'build ' found at position 0.
Imports System.Text.RegularExpressions

Module RTL1Example
    Public Sub Main()
        Dim pattern As String = "\bb\w+\s"
        Dim input As String = "build band tab"
        For Each match As Match In Regex.Matches(input, pattern, RegexOptions.RightToLeft)
            Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
        Next
    End Sub
End Module
' The example displays the following output:
'       'band ' found at position 6.
'       'build ' found at position 0.

評估順序

選項 RegexOptions.RightToLeft 會變更搜尋方向,也會反轉規則運算式模式評估的順序。 在由右至左的搜尋中,會從右到左讀取搜尋模式。 這項區別很重要,因為它可能會影響擷取群組和反向參考之類的事項。 例如,運算式 Regex.Match("abcabc", @"\1(abc)", RegexOptions.RightToLeft) 會尋找相符的 abcabc,但在從左至右搜尋 (Regex.Match("abcabc", @"\1(abc)", RegexOptions.None)) 中,找不到相符項目。 這是因為 (abc) 項目必須在編號擷取群組項目 (\1) 之前進行評估,才能找到相符項目。

預查和後查斷言

前瞻((?=subexpression))或後顧((?<=subexpression))判斷的符合條件的位置在從右至左的搜尋中不會改變。 前瞻判斷會查看目前匹配位置的右側;後顧判斷會查看目前匹配位置的左側。

提示

不論搜尋方向是否為由右至左,後向檢索皆會使用從目前匹配位置開始的由右至左搜尋來實作。

例如,規則運算式 (?<=\d{1,2}\s)\w+,\s\d{4} 使用後視回溯判斷來測試位於月份名稱之前的日期。 然後正則表達式會匹配月份和年份。 如需有關前瞻和後顧判斷的詳細資訊,請參閱群組建構

using System;
using System.Text.RegularExpressions;

public class RTL2Example
{
    public static void Main()
    {
        string[] inputs = { "1 May, 1917", "June 16, 2003" };
        string pattern = @"(?<=\d{1,2}\s)\w+,\s\d{4}";

        foreach (string input in inputs)
        {
            Match match = Regex.Match(input, pattern, RegexOptions.RightToLeft);
            if (match.Success)
                Console.WriteLine($"The date occurs in {match.Value}.");
            else
                Console.WriteLine($"{input} does not match.");
        }
    }
}

// The example displays the following output:
//       The date occurs in May, 1917.
//       June 16, 2003 does not match.
Imports System.Text.RegularExpressions

Module RTL2Example
    Public Sub Main()
        Dim inputs() As String = {"1 May, 1917", "June 16, 2003"}
        Dim pattern As String = "(?<=\d{1,2}\s)\w+,\s\d{4}"

        For Each input As String In inputs
            Dim match As Match = Regex.Match(input, pattern, RegexOptions.RightToLeft)
            If match.Success Then
                Console.WriteLine("The date occurs in {0}.", match.Value)
            Else
                Console.WriteLine("{0} does not match.", input)
            End If
        Next
    End Sub
End Module

' The example displays the following output:
'       The date occurs in May, 1917.
'       June 16, 2003 does not match.

規則運算式模式 的定義如下表所示。

模式 描述
(?<=\d{1,2}\s) 比賽的開始必須要有一個或兩個十進位數字,然後接空格。
\w+ 比對一個或多個文字字元。
, 匹配一個逗號字元。
\s 比對空白字元。
\d{4} 比對四個十進位數字。

ECMAScript 比對行為

依預設,在比對規則運算式模式與輸入文字時,規則運算式引擎會使用標準行為。 不過,您可以指定 RegexOptions.ECMAScript 選項,來使正則表達式引擎使用 ECMAScript 相符行為。

注意

若要使用符合 ECMAScript 的行為,唯一的方法就是提供 RegexOptions.ECMAScript 值給 options 類別建構函式或靜態模式比對方法的 Regex 參數。 無法作為內嵌選項供應。

RegexOptions.ECMAScript選項只能與RegexOptions.IgnoreCaseRegexOptions.MultilineRegexOptions.Compiled選項結合使用。 在規則運算式中使用任何其他選項將會導致 ArgumentOutOfRangeException

ECMAScript 的行為與標準規則運算式有三個不同層面:字元類別語法、自我參考擷取群組,以及八進位與反向參考解譯。

  • 字元類別語法。 因為標準規則運算式支援 Unicode,而 ECMAScript 不支援,所以 ECMAScript 中的字元類別有較多的語法限制,而且有些字元類別語言項目有不同的意義。 例如,ECMAScript 不支援語言項目 (例如 Unicode 類別) 或資料區塊項目 \p\P。 同樣地,使用 ECMAScript 時,\w 項目 (用來比對文字字元) 同等於 [a-zA-Z_0-9] 字元類別,使用標準行為時,同等於 [\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}]。 如需詳細資訊,請參閱字元類別

    下列範例說明標準與 ECMAScript 模式比對之間的差異。 它定義了一個正則表達式 \b(\w+\s*)+,用於匹配後接空白字元的單詞。 該輸入包含兩個字串,一個使用 Latin 字元集,另一個使用 Cyrillic 字元集。 如輸出所示,呼叫使用 ECMAScript 比對的 Regex.IsMatch(String, String, RegexOptions) 方法時,無法比對 Cyrillic 文字,而使用標準比對的方法呼叫則可比對這些文字。

    using System;
    using System.Text.RegularExpressions;
    
    public class EcmaScriptExample
    {
        public static void Main()
        {
            string[] values = { "целый мир", "the whole world" };
            string pattern = @"\b(\w+\s*)+";
            foreach (var value in values)
            {
                Console.Write("Canonical matching: ");
                if (Regex.IsMatch(value, pattern))
                    Console.WriteLine($"'{value}' matches the pattern.");
                else
                    Console.WriteLine($"{value} does not match the pattern.");
    
                Console.Write("ECMAScript matching: ");
                if (Regex.IsMatch(value, pattern, RegexOptions.ECMAScript))
                    Console.WriteLine($"'{value}' matches the pattern.");
                else
                    Console.WriteLine($"{value} does not match the pattern.");
                Console.WriteLine();
            }
        }
    }
    // The example displays the following output:
    //       Canonical matching: 'целый мир' matches the pattern.
    //       ECMAScript matching: целый мир does not match the pattern.
    //
    //       Canonical matching: 'the whole world' matches the pattern.
    //       ECMAScript matching: 'the whole world' matches the pattern.
    
    Imports System.Text.RegularExpressions
    
    Module Ecma1Example
        Public Sub Main()
            Dim values() As String = {"целый мир", "the whole world"}
            Dim pattern As String = "\b(\w+\s*)+"
            For Each value In values
                Console.Write("Canonical matching: ")
                If Regex.IsMatch(value, pattern) Then
                    Console.WriteLine("'{0}' matches the pattern.", value)
                Else
                    Console.WriteLine("{0} does not match the pattern.", value)
                End If
    
                Console.Write("ECMAScript matching: ")
                If Regex.IsMatch(value, pattern, RegexOptions.ECMAScript) Then
                    Console.WriteLine("'{0}' matches the pattern.", value)
                Else
                    Console.WriteLine("{0} does not match the pattern.", value)
                End If
                Console.WriteLine()
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       Canonical matching: 'целый мир' matches the pattern.
    '       ECMAScript matching: целый мир does not match the pattern.
    '       
    '       Canonical matching: 'the whole world' matches the pattern.
    '       ECMAScript matching: 'the whole world' matches the pattern.
    
  • 自我參考擷取群組。 具有自我反向參考的正則表達式擷取類別必須在每次擷取迭代時進行更新。 如下列範例所示,使用 ECMAScript 時,此功能可讓規則運算式 ((a+)(\1) ?)+ 比對輸入字串 " aa aaaa aaaaaa ",使用標準比對時則不能。

    using System;
    using System.Text.RegularExpressions;
    
    public class EcmaScript2Example
    {
        static string pattern;
    
        public static void Main()
        {
            string input = "aa aaaa aaaaaa ";
            pattern = @"((a+)(\1) ?)+";
    
            // Match input using canonical matching.
            AnalyzeMatch(Regex.Match(input, pattern));
    
            // Match input using ECMAScript.
            AnalyzeMatch(Regex.Match(input, pattern, RegexOptions.ECMAScript));
        }
    
        private static void AnalyzeMatch(Match m)
        {
            if (m.Success)
            {
                Console.WriteLine($"'{pattern}' matches {m.Value} at position {m.Index}.");
                int grpCtr = 0;
                foreach (Group grp in m.Groups)
                {
                    Console.WriteLine($"   {grpCtr}: '{grp.Value}'");
                    grpCtr++;
                    int capCtr = 0;
                    foreach (Capture cap in grp.Captures)
                    {
                        Console.WriteLine($"      {capCtr}: '{cap.Value}'");
                        capCtr++;
                    }
                }
            }
            else
            {
                Console.WriteLine("No match found.");
            }
            Console.WriteLine();
        }
    }
    // The example displays the following output:
    //    No match found.
    //
    //    '((a+)(\1) ?)+' matches aa aaaa aaaaaa  at position 0.
    //       0: 'aa aaaa aaaaaa '
    //          0: 'aa aaaa aaaaaa '
    //       1: 'aaaaaa '
    //          0: 'aa '
    //          1: 'aaaa '
    //          2: 'aaaaaa '
    //       2: 'aa'
    //          0: 'aa'
    //          1: 'aa'
    //          2: 'aa'
    //       3: 'aaaa '
    //          0: ''
    //          1: 'aa '
    //          2: 'aaaa '
    
    Imports System.Text.RegularExpressions
    
    Module Ecma2Example
        Dim pattern As String
    
        Public Sub Main()
            Dim input As String = "aa aaaa aaaaaa "
            pattern = "((a+)(\1) ?)+"
    
            ' Match input using canonical matching.
            AnalyzeMatch(Regex.Match(input, pattern))
    
            ' Match input using ECMAScript.
            AnalyzeMatch(Regex.Match(input, pattern, RegexOptions.ECMAScript))
        End Sub
    
        Private Sub AnalyzeMatch(m As Match)
            If m.Success Then
                Console.WriteLine("'{0}' matches {1} at position {2}.",
                                  pattern, m.Value, m.Index)
                Dim grpCtr As Integer = 0
                For Each grp As Group In m.Groups
                    Console.WriteLine("   {0}: '{1}'", grpCtr, grp.Value)
                    grpCtr += 1
                    Dim capCtr As Integer = 0
                    For Each cap As Capture In grp.Captures
                        Console.WriteLine("      {0}: '{1}'", capCtr, cap.Value)
                        capCtr += 1
                    Next
                Next
            Else
                Console.WriteLine("No match found.")
            End If
            Console.WriteLine()
        End Sub
    End Module
    ' The example displays the following output:
    '    No match found.
    '    
    '    '((a+)(\1) ?)+' matches aa aaaa aaaaaa  at position 0.
    '       0: 'aa aaaa aaaaaa '
    '          0: 'aa aaaa aaaaaa '
    '       1: 'aaaaaa '
    '          0: 'aa '
    '          1: 'aaaa '
    '          2: 'aaaaaa '
    '       2: 'aa'
    '          0: 'aa'
    '          1: 'aa'
    '          2: 'aa'
    '       3: 'aaaa '
    '          0: ''
    '          1: 'aa '
    '          2: 'aaaa '
    

    規則運算式的定義如下表所示。

    模式 描述
    (a+) 比對字母 "a" 一次或多次。 這是第二個擷取群組。
    (\1) 比對第一個擷取群組所擷取的子字串。 這是第三個擷取群組。
    ? 比對零或一個空白字元。
    ((a+)(\1) ?)+ 多次比對下列模式:一個或多個 "a" 字元,接著符合第一個擷取群組的字串,再接著零或一個空白字元。 這是第一個擷取群組。
  • 八進位逸出與反向參考之間模棱兩可的解決方案。 下表總結了正規表示式中標準形式與 ECMAScript 形式對八進位和反向參考解釋的差異。

    規則運算式 標準行為 ECMAScript 行為
    \0 後接 0 到 2 個八進位數字 解譯成八進位。 例如,\044 一律解譯成八進位值,且意思是 "$"。 相同行為。
    \ 後接 1 到 9 的數字,後面不再接其他十進位數字。 解譯成反向參考。 例如,\9 一律表示反向參考 9,即使第 9 個擷取群組不存在也一樣。 如果擷取群組不存在,規則運算式剖析器會拋出 ArgumentException 如果單一十進位數字擷取群組存在,則反向參考至該數字。 否則,會將該值解譯成常值。
    \ 後接 1 到 9 的數字,後面再接其他十進位數字。 將這些數字解譯成十進位值。 如果該擷取群組存在,則將運算式視為反引用。

    否則,解譯前置八進位數字至八進位 377;也就是說,僅考慮該值的低 8 位元。 將其餘數字解譯成常值。 例如,在運算式 \3000 中,如果擷取群組 300 存在,則解譯成反向參考 300;如果擷取群組 300 不存在,則解譯成八進位 300 後接 0。
    透過將盡可能多的數字轉換為可以參考擷取的十進位值來理解為反向引用。 如果沒有數字可供轉換,則使用前置八進位數字至八進位 377,以解譯成八進位;將其餘數字解譯成常值。

使用不變文化進行比較

依預設,當規則運算式引擎執行不區分大小寫比較時,會使用目前文化特性的大小寫慣例來判定相等的大小寫字元。

然而,這種行為對某些比較類型來說是不理想的,尤其是在將使用者輸入與系統資源名稱(例如密碼、檔案或 URL)進行比較時。 下列範例說明這種案例。 程式碼的目的是要封鎖存取前面加上 FILE:// 之 URL 的任何資源。 正則表達式 $FILE:// 嘗試與字串進行不區分大小寫的匹配。 不過,當目前系統文化特性為 tr-TR (Turkish-Türkiye) 時,"I" 並不是 "i" 的大寫代表字元。 因此,呼叫 Regex.IsMatch 方法時,會傳回 false,並允許存取該檔案。

CultureInfo defaultCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");

string input = "file://c:/Documents.MyReport.doc";
string pattern = "FILE://";

Console.WriteLine($"Culture-sensitive matching ({Thread.CurrentThread.CurrentCulture.Name} culture)...");
if (Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase))
    Console.WriteLine("URLs that access files are not allowed.");
else
    Console.WriteLine($"Access to {input} is allowed.");

Thread.CurrentThread.CurrentCulture = defaultCulture;
// The example displays the following output:
//       Culture-sensitive matching (tr-TR culture)...
//       Access to file://c:/Documents.MyReport.doc is allowed.
Dim defaultCulture As CultureInfo = Thread.CurrentThread.CurrentCulture
Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR")

Dim input As String = "file://c:/Documents.MyReport.doc"
Dim pattern As String = "$FILE://"

Console.WriteLine("Culture-sensitive matching ({0} culture)...",
                  Thread.CurrentThread.CurrentCulture.Name)
If Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase) Then
    Console.WriteLine("URLs that access files are not allowed.")
Else
    Console.WriteLine("Access to {0} is allowed.", input)
End If

Thread.CurrentThread.CurrentCulture = defaultCulture
' The example displays the following output:
'       Culture-sensitive matching (tr-TR culture)...
'       Access to file://c:/Documents.MyReport.doc is allowed.

注意

如需區分大小寫和使用不變文化的字串比較的詳細資訊,請參閱使用字串的最佳作法

您可以選擇不使用當前文化的不區分大小寫比較,而是指定 RegexOptions.CultureInvariant 選項,以忽略語言中的文化差異並使用不變文化的慣例。

注意

若要使用不因國別而異的文化特性進行比較,唯一的方法是將 RegexOptions.CultureInvariant 值提供給 options 類別建構函式或靜態模式比對方法的 Regex 參數。 無法作為內嵌選項供應。

下列範例與上一個範例相同,差別在於呼叫靜態 Regex.IsMatch(String, String, RegexOptions) 方法時,是使用包含 RegexOptions.CultureInvariant 的選項。 即使將語言環境設為土耳其語(土耳其),正規表示式引擎仍然可以成功比對"FILE"和"file",並封鎖對檔案資源的存取。

CultureInfo defaultCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");

string input = "file://c:/Documents.MyReport.doc";
string pattern = "FILE://";

Console.WriteLine("Culture-insensitive matching...");
if (Regex.IsMatch(input, pattern,
                  RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
    Console.WriteLine("URLs that access files are not allowed.");
else
    Console.WriteLine($"Access to {input} is allowed.");

Thread.CurrentThread.CurrentCulture = defaultCulture;
// The example displays the following output:
//       Culture-insensitive matching...
//       URLs that access files are not allowed.
Dim defaultCulture As CultureInfo = Thread.CurrentThread.CurrentCulture
Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR")

Dim input As String = "file://c:/Documents.MyReport.doc"
Dim pattern As String = "$FILE://"

Console.WriteLine("Culture-insensitive matching...")
If Regex.IsMatch(input, pattern,
               RegexOptions.IgnoreCase Or RegexOptions.CultureInvariant) Then
    Console.WriteLine("URLs that access files are not allowed.")
Else
    Console.WriteLine("Access to {0} is allowed.", input)
End If
Thread.CurrentThread.CurrentCulture = defaultCulture
' The example displays the following output:
'        Culture-insensitive matching...
'        URLs that access files are not allowed.

非回溯模式

預設情況下,.NET 的正則表達式引擎使用 backtracking來嘗試尋找模式匹配。 回溯引擎是嘗試比對一個模式的引擎,如果失敗,則會返回並嘗試比對替代模式,依此類推。 回溯引擎在典型情況下非常快速,但隨著模式替換數目的增加而變慢,這可能會導致災難性的回溯RegexOptions.NonBacktracking 選項在 .NET 7 中引入,不使用回溯,避免了最壞的情況。 其目標是提供始終如一的良好表現,無論搜尋的輸入內容為何。

RegexOptions.NonBacktracking 選項不支援其他內建引擎支援的所有內容。 特別地,該選項不能與 、 RegexOptions.RightToLeft、 或 RegexOptions.ECMAScript同時使用RegexOptions.AnyNewLine。 它也不允許在模式中使用下列建構:

  • 原子群組
  • 反向參考
  • 平衡群組
  • 條件語句
  • 環顧四周
  • 開始錨點 (\G)

RegexOptions.NonBacktracking 在執行方面也有細微的差異。 如果擷取群組處於迴圈中,大多數(非 .NET)正則表達式引擎只會提供該擷取的最後匹配值。 然而,.NET 的正則表達式引擎會追蹤迴圈中捕捉的所有值並提供存取權限。 此 RegexOptions.NonBacktracking 選項就像大部分其他 RegEx 實作一樣,而且只支援提供最終擷取。

如需回溯的詳細資訊,請參閱規則運算式中的回溯

AnyNewLine 模式

預設情況下,.NET 的正則表達式引擎只將 \n 視為換行字元。 錨點 ^$(在 RegexOptions.Multiline 模式中),\Z,以及萬用字元 . 都使用 \n 作為唯一的行界限。 這表示 $\r\n(Windows 風格行尾)之前無法匹配,而 . 則無法匹配 \r,但無法匹配 \n(除非啟用 RegexOptions.Singleline,此時. 匹配所有字元),導致處理混合行尾或非 Unix 字尾的文字時常見錯誤。

RegexOptions.AnyNewLine選項於.NET 11引入,使這些結構能識別所有常見的換行序列:\r\n (CR+LF)、\r (CR)、\n (LF)、\u0085 (NEL)、\u2028 (LS)及 \u2029 (PS)。 這與 Unicode TR18 RL1.6 是一致的。

例如,沒有 AnyNewLine 的情況下,要在字串中匹配 Windows 行尾的行,可能需要手動處理,例如使用 \r?$ 這樣的方式:

// BUG: .+$ captures trailing \r on Windows line endings
var match = Regex.Match("foo\r\nbar", @".+$", RegexOptions.Multiline);
Console.WriteLine(match.Value); // "foo\r" -- not "foo"!

有了 AnyNewLine,錨點自動處理所有換行類型:

var match = Regex.Match("foo\r\nbar", @".+$",
    RegexOptions.Multiline | RegexOptions.AnyNewLine);
Console.WriteLine(match.Value); // "foo"

下表總結了 AnyNewLine 對每個構念的影響:

構建 預設行為 使用 AnyNewLine
. (預設值) 匹配任何字元,除外 \n 匹配除 \r\n\u0085\u2028\u2029 之外的任何字元。
$ (多行) 之前的比賽 \n 比賽前 \r\n\r\n\u0085\u2028\u2029
^ (多行) 比賽結束後 \n 匹配後 \r\n\r\n\u0085\u2028\u2029
$ (預設) / \Z 字串結尾 \n 前的匹配 字串末尾的任何換行序列之前的比對

設計重點:

  • \r\n會被以原子方式處理$會在完整的\r\n序列之前匹配,從不會在\r\n之間匹配。
  • Singleline 優先權. 兩者同時SinglelineAnyNewLine皆符合所有字元(包括換行),並與Singleline的現有行為一致。
  • \A\z 不受影響:絕對的字串起始與結尾錨點不變。
  • 不相容的選項AnyNewLine 無法與 RegexOptions.NonBacktrackingRegexOptions.ECMAScript結合。 嘗試這樣做會產生 ArgumentOutOfRangeException

另請參閱