Поделиться через


Параметры регулярных выражений

По умолчанию сравнение входной строки с любыми литеральными символами в шаблоне регулярного выражения учитывает регистр, пробелы в шаблоне регулярного выражения интерпретируются как литеральные символы пробелов, а группы захвата в регулярном выражении могут именоваться автоматически, а также явно. Вы можете изменить эти и некоторые другие аспекты поведения регулярного выражения по умолчанию с помощью параметров регулярного выражения. Некоторые из этих параметров, перечисленных в следующей таблице, могут быть включены в состав шаблона регулярных выражений, или они могут быть предоставлены System.Text.RegularExpressions.Regex конструктору класса или методу System.Text.RegularExpressions.RegexOptions сопоставления статических шаблонов в качестве значения перечисления.

ЧленRegexOptions Встроенный символ Действие Дополнительные сведения
None Недоступно Использовать поведение по умолчанию. Параметры по умолчанию
IgnoreCase i Использовать сравнение без учета регистра. Сопоставление без учета регистра
Multiline m Используйте многострочный режим, где ^ и $ укажите начало и конец каждой строки (вместо начала и конца входной строки). Многострочный режим
Singleline s Использовать однострочный режим, где точка (.) соответствует любому символу (а не каждому символу, кроме \n). Однострочный режим
ExplicitCapture n Не захватывать неименованные группы. К допустимым захватам относятся только явно именованные или нумерованные группы в формате (?<имя>часть выражения). Только явные записи
Compiled Недоступно Скомпилировать регулярное выражение в сборку. Скомпилированные регулярные выражения
IgnorePatternWhitespace x Исключить неэкранированные пробелы из шаблона и включить комментарии после символа решетки (#). Игнорировать пробелы
RightToLeft Недоступно Изменить направление поиска. Поиск идет справа налево, а не слева направо. Режим справа налево
ECMAScript Недоступно Включить поведение, совместимое с ECMAScript, для выражения. Поведение сопоставления ECMAScript
CultureInvariant Недоступно Игнорировать культурные различия в языке. Сравнение с использованием инвариантной культуры
NonBacktracking Недоступно Сопоставление с использованием подхода, который позволяет избежать обратного отслеживания и гарантирует обработку за линейное время относительно длины входных данных. (Доступно в .NET 7 и более поздних версиях.) Режим без возврата

Указание параметров

Параметры регулярных выражений можно указать одним из трех способов:

  • В параметре options конструктора класса System.Text.RegularExpressions.Regex или статичного метода сопоставления шаблона (Shared в Visual Basic), например 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.      
    
  • Применяя встроенные параметры в определенной конструкции группировки в шаблоне регулярного выражения с помощью синтаксиса (?imnsx-imnsx:часть выражения). Если перед набором параметров знак отсутствует, он включается; если перед набором параметров стоит знак минуса, набор отключается. (? является фиксированной частью синтаксиса языковой конструкции, которая требуется независимо от того, включены или отключены параметры.) Этот параметр применяется только к этой группе. Дополнительные сведения см. в разделе Конструкции группировки.

    Это показывается в следующем примере. В нем используются встроенные опции в конструкции группировки для включения сопоставления без учета регистра и игнорирования пробелов в шаблоне при определении слов, начинающихся с буквы "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.IgnoreCase и RegexOptions.IgnorePatternWhitespace и отключает параметры RegexOptions.Multiline и RegexOptions.Singleline. Все параметры регулярного выражения по умолчанию отключены.

Примечание.

Если параметры регулярного выражения, указанные в параметре options конструктора или вызове метода, конфликтуют со встроенными параметрами в шаблоне регулярного выражения, используются последние.

Следующие пять параметров регулярного выражения можно задавать одновременно в параметрах метода и в строке:

Следующие пять параметров регулярного выражения можно задавать в параметре options, но не в строке:

Определение вариантов

Вы можете определить, какие параметры были предоставлены объекту Regex при создании его экземпляра, получив значение свойства Regex.Options только для чтения.

Чтобы проверить наличие любого параметра, кроме RegexOptions.None, выполните операцию AND со значением свойства Regex.Options и значением RegexOptions, которое вас интересует. Затем проверьте, равен ли результат значению 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, обеспечивая сравнение без учета регистра. Первый шаблон определяет регистронезависимую опцию в конструкции группировки, касающейся только буквы "t" в строке "the". Так как конструкция указана в начале шаблона, второй шаблон применяет параметр игнорирования регистра ко всему регулярному выражению.

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 часть матча.

Следующий пример извлекает имена и баллы боулингистов и добавляет их в коллекцию 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\.

Только явные захваты

По умолчанию захватываемые группы определяются с помощью круглых скобок в шаблоне регулярного выражения. Именованным группам назначается имя или номер с помощью параметра языка (?<имя>часть выражения). Неименованные группы доступны по индексу. В объекте GroupCollection неименованные группы идут перед именованными.

Конструкции группировки часто используются только для применения квантификаторов к нескольким языковым элементам, а захваченные подстроки не представляют интереса. Например, если следующее регулярное выражение,

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

Предназначено только для извлечения из документа предложений, которые оканчиваются на точку, восклицательный или вопросительный знак. Интерес представляет только полученное предложение (в объекте Match). а отдельные слова в коллекции — нет.

Захватываемые группы, которые не используются впоследствии, могут потреблять много ресурсов, так как механизм регулярных выражений должен заполнить объекты GroupCollection и CaptureCollection коллекции. В качестве альтернативы можно использовать параметр RegexOptions.ExplicitCapture или встроенный параметр n, чтобы указать, что допустимыми захватами являются только явно именованные или нумерованные группы, обозначенными конструкцией (?<имя>часть выражения).

Следующий пример отображает сведения о сопоставлениях, возвращаемых шаблоном регулярного выражения \b\(?((\w+),?\s?)+[\.!?]\)?, если метод Match вызывается с параметром RegexOptions.ExplicitCapture или без него. Как видно в результатах выполнения первого вызова метода, механизм регулярных выражений полностью заполняет объекты коллекции GroupCollection и CaptureCollection данными о выделенных подстроках. Так как второй метод вызывается с параметром 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, шаблон регулярного выражения преобразуется в набор настраиваемых кодов операций, а интерпретатор использует их для выполнения регулярного выражения. Это связано с компромиссом: стоимость инициализации подсистемы регулярных выражений сводится к минимуму за счет производительности во время выполнения.

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

Примечание.

Чтобы скомпилировать регулярное выражение, необходимо передать значение 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}, представляющий категорию Юникода или именованный блок, не может содержать пробелы в части \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) для поиска соответствия.

Утверждения Lookahead и lookbehind

Расположение совпадения для утверждения lookahead ((?=subexpression)) или lookbehind ((?<=subexpression)) не изменяется в поиске справа налево. Утверждения предварительного просмотра проверяют вправо от текущего расположения совпадения; утверждения отложенного просмотра проверяют влево от текущего расположения совпадения.

Совет

Независимо от того, является ли поиск справа налево или нет, ретроспективы реализуются с помощью поиска справа налево, начиная с текущего места совпадения.

Например, регулярное выражение (?<=\d{1,2}\s)\w+,\s\d{4} использует оператор lookbehind для проверки даты, идущей перед названием месяца. Затем регулярное выражение сопоставляет месяц и год. Сведения об условных выражениях предсмотр и послесмотр см. в статье Конструкции группировки.

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} Совпадают 4 десятичных цифры.

Поведение сопоставления ECMAScript

По умолчанию механизм регулярных выражений использует каноническое поведение при сопоставлении шаблона регулярного выражения с входным текстом. Однако вы можете указать движку регулярных выражений использовать поведение сопоставления ECMAScript, задав параметр RegexOptions.ECMAScript.

Примечание.

Чтобы включить поведение ECMAScript, необходимо передать значение RegexOptions.ECMAScript параметру options конструктора класса Regex или статичного метода сопоставления шаблона. Этот параметр не может быть использован в качестве встроенной опции.

Параметр RegexOptions.ECMAScript может использоваться только вместе с параметрами RegexOptions.IgnoreCase и RegexOptions.Multiline. При использовании любого другого варианта в регулярном выражении возникает исключение ArgumentOutOfRangeException.

Поведение ECMAScript и канонических регулярных выражений отличается в трех аспектах: синтаксис класса символов, самоссылающиеся группы захвата и интерпретация восьмеричных значений в сравнении с обратными ссылками.

  • Синтаксис класса символов. Так как канонические регулярные выражения поддерживают Юникод, а ECMAScript — нет, синтаксис классов символов в ECMAScript более ограничен, а некоторые языковые элементы класса символов обладают другим значением. Например, ECMAScript не поддерживает такие языковые элементы, как категория Юникода или элементы блока \p и \P. Аналогичным образом, элемент \w, который сопоставляет символ слова, эквивалентен классу символов [a-zA-Z_0-9] при использовании ECMAScript и [\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}] при стандартном поведении. Дополнительные сведения см. в разделе Классы символов.

    Следующий пример иллюстрирует разницу между каноническим соответствием шаблону и ECMAScript. В нем определяется регулярное выражение, \b(\w+\s*)+, сопоставляющее слова, за которыми следуют пробелы. Входные данные состоят из двух строк, одна из которых использует латиницу, а другая — кириллицу. Как видно из результата, при вызове метода Regex.IsMatch(String, String, RegexOptions), использующего сопоставление ECMAScript, не удается сопоставить слова на кириллице, а при вызове метода, использующего каноническое сопоставление — удается.

    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.
    
  • Самоссылочные группы захвата Класс захвата регулярного выражения с обратной ссылкой на себя необходимо обновлять при каждой итерации захвата. Как показано в следующем примере, эта функция позволяет регулярному выражению ((a+)(\1) ?)+ сопоставлять входную строку "aa aaaa aaaaaa" при использовании ECMAScript, но не при каноническом сопоставлении.

    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", за которыми следует строка, сопоставляющая первую захватываемую группу, за которой следует ноль или один символ пробела. Это первая группа захвата.
  • Разрешение неоднозначности между восьмеричными Escape-символами и обратными ссылками. В следующей таблице представлены общие сведения об отличиях интерпретации восьмеричных чисел и обратных ссылок при использовании канонических регулярных выражений и ECMAScript.

    Регулярное выражение Каноническое поведение Поведение ECMAScript
    \0 с последующими 0-2 восьмеричными цифрами Интерпретируется как восьмеричное число. Например, \044 всегда интерпретируется как восьмеричное значение и означает "$". Такое же поведение.
    \ с последующей цифрой от 1 до 9, за которой нет дополнительных десятичных цифр. Интерпретируется как обратная ссылка. Например, \9 всегда означает обратную ссылку на 9, даже если девятая захватываемая группа не существует. Если захватываемая группа не существует, парсер регулярных выражений бросает исключение ArgumentException. Если существует захватываемая группа с одной десятичной цифрой, сделайте обратную ссылку на эту цифру. В противном случае значение интерпретируется как литерал.
    \ с последующей цифрой от 1 до 9, за которой следуют дополнительные десятичные цифры. Цифры интерпретируются как десятичное значение. Если эта захватываемая группа существует, интерпретируйте выражение как обратную ссылку.

    В противном случае интерпретируются первые восьмеричные цифры до восьмеричного числа 377, т. е. учитываются только младшие 8 разрядов значения. Оставшиеся цифры интерпретируются как литералы. Например, выражение \3000, если захватываемая группа 300 существует, интерпретируется как обратная ссылка на 300. В противном случае оно интерпретируется как восьмеричное число 300, за которым следует 0.
    Выражение интерпретируется как обратная ссылка, для этого как можно больше цифр преобразуется в десятичное значение, которые могут указывать на выделение. Если цифры не могут быть преобразованы, выражение интерпретируется как восьмеричное число с использованием первых восьмеричных разрядов до восьмеричного числа 377. Оставшиеся восьмеричные цифры интерпретируются как литералы.

Сравнение с использованием инвариантной культуры

По умолчанию, когда модуль регулярных выражений выполняет сравнения без учета регистра, правила использования регистра в текущих региональных параметрах используются для определения эквивалентных прописных и строчных символов.

Однако это недопустимо для некоторых типов сравнения, например при сравнении введенных пользователем данных с именами системных ресурсов, таких как пароли, файлы и URL-адреса. В следующем примере показан такой сценарий. Этот код предназначен для блокировки доступа к любым ресурсам, URL-адрес которых начинается с FILE://. Регулярное выражение пытается выполнить сопоставление без учета чувствительности к регистру, используя выражение $FILE://. Однако, если настройки текущей культуры системы заданы как tr-TR (Turkish-Турция), буква "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. Даже когда текущий язык и региональные настройки установлены на турецкий (Türkiye), механизм регулярных выражений может успешно совпадать с 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 использует бэктрекинг для поиска соответствий шаблону. Подсистема обратного отслеживания — это механизм, который пытается соответствовать одному шаблону, и если это не удается, возвращается и пытается сопоставить альтернативный шаблон и т. д. Механизм обратного отслеживания очень быстр для типичных случаев, но замедляется по мере увеличения числа чередований шаблонов, что может привести к катастрофическому обратному отслеживанию. Этот RegexOptions.NonBacktracking вариант, который был представлен в .NET 7, не использует бэктрекинг и избегает наихудших сценариев. Ее целью является обеспечение стабильного хорошего поведения независимо от искомых входных данных.

Этот RegexOptions.NonBacktracking параметр не поддерживает все возможности, которые поддерживаются другими встроенными механизмами. В частности, параметр нельзя использовать в сочетании с RegexOptions.RightToLeft или RegexOptions.ECMAScript. Он также не допускает следующие конструкции в шаблоне:

  • Атомарные группы
  • Обратные ссылки
  • Группы балансировки
  • Условные выражения
  • Обходные пути
  • Запуск якорей (\G)

RegexOptions.NonBacktracking также имеет тонкое различие в отношении исполнения. Если группа захвата находится в цикле, большинство механизмов регулярных выражений (non-.NET) предоставляют только последнее соответствующее значение для этого захвата. Однако обработчик регулярных выражений .NET отслеживает все значения, которые захватываются в цикле, и предоставляет доступ к ним. Этот RegexOptions.NonBacktracking вариант, как и большинство других реализаций регексп, поддерживает только предоставление конечного результата захвата.

Дополнительные сведения об обратном отслеживании см. в разделе Backtracking в регулярных выражениях.

См. также