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

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

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

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

  • В параметре 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("'{0}// found at index {1}.", match.Value, 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("'{0}// found at index {1}.", match.Value, 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("'{0}// found at index {1}.", match.Value, 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 только для чтения. Это свойство, в частности, полезно для определения параметров, заданных для скомпилированного регулярного выражения, которое было создано методом Regex.CompileToAssembly.

Чтобы проверить наличие любого параметра, кроме 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 {0} at index {1}.", match.Value, match.Index);

      Console.WriteLine();
      foreach (Match match in Regex.Matches(input, pattern,
                                            RegexOptions.IgnoreCase))
         Console.WriteLine("Found {0} at index {1}.", match.Value, 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 {0} at index {1}.", match.Value, match.Index);

        Console.WriteLine();
        pattern = @"(?i)\bthe\w*\b";
        foreach (Match match in Regex.Matches(input, pattern,
                                              RegexOptions.IgnoreCase))
            Console.WriteLine("Found {0} at index {1}.", match.Value, 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("{0}: {1}", 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*$ определяется, как показано в следующей таблице.

Расписание Description
^ Начало с первого символа строки.
(\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("{0}: {1}", 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: {0}", match.Value);
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value);
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine("      Capture {0}: {1}", 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: {0}", match.Value);
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value);
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine("      Capture {0}: {1}", 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?)+[\.!?]\)? определяется, как показано в следующей таблице.

Расписание Description
\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: {0}", match.Value);
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value);
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine("      Capture {0}: {1}", 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: {0}", match.Value);
            int groupCtr = 0;
            foreach (Group group in match.Groups)
            {
                Console.WriteLine("   Group {0}: {1}", groupCtr, group.Value);
                groupCtr++;
                int captureCtr = 0;
                foreach (Capture capture in group.Captures)
                {
                    Console.WriteLine("      Capture {0}: {1}", 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("'{0}' found at position {1}.", match.Value, 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 () или lookbehind ((?=subexpression)(?<=subexpression)) не изменяется в поиске справа налево. Утверждения lookahead выглядят справа от текущего расположения совпадения; Утверждения lookbehind выглядят слева от текущего расположения соответствия.

Совет

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

Например, регулярное выражение (?<=\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 {0}.", match.Value);
            else
                Console.WriteLine("{0} does not match.", input);
        }
    }
}

// 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.

Шаблон регулярного выражения определяется, как показано в следующей таблице.

Расписание Description
(?<=\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("'{0}' matches the pattern.", value);
                else
                    Console.WriteLine("{0} does not match the pattern.", value);
    
                Console.Write("ECMAScript matching: ");
                if (Regex.IsMatch(value, pattern, RegexOptions.ECMAScript))
                    Console.WriteLine("'{0}' matches the pattern.", value);
                else
                    Console.WriteLine("{0} does not match the pattern.", value);
                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("'{0}' matches {1} at position {2}.",
                                  pattern, m.Value, m.Index);
                int grpCtr = 0;
                foreach (Group grp in m.Groups)
                {
                    Console.WriteLine("   {0}: '{1}'", grpCtr, grp.Value);
                    grpCtr++;
                    int capCtr = 0;
                    foreach (Capture cap in grp.Captures)
                    {
                        Console.WriteLine("      {0}: '{1}'", 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 '
    

    Определение регулярного выражения показано в следующей таблице.

    Расписание Description
    (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-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 ({0} culture)...",
                  Thread.CurrentThread.CurrentCulture.Name);
if (Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase))
    Console.WriteLine("URLs that access files are not allowed.");
else
    Console.WriteLine("Access to {0} is allowed.", input);

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.

Примечание.

Подробнее о сравнении строк с учетом регистра и использовании инвариантных региональных параметров см. в разделе Рекомендации по использованию строк в .NET.

Вместо использования сравнений без учета регистра текущих региональных параметров, можно указать параметр 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 {0} is allowed.", input);

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.

Режим nonbacktracking

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

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

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

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

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

См. также