Подробные сведения о поведении регулярных выраженийDetails of Regular Expression Behavior

Обработчик регулярных выражений .NET Framework выполняет поиск с возвратом для регулярных выражений и является реализацией традиционного механизма NFA (недетерминированного конечного автомата), аналогично тем, которые используются в Perl, Python, Emacs и Tcl.The .NET Framework regular expression engine is a backtracking regular expression matcher that incorporates a traditional Nondeterministic Finite Automaton (NFA) engine such as that used by Perl, Python, Emacs, and Tcl. Это отличает его от более быстрых, но и более ограниченных DFA-машин (детерминированный конечный автомат), предназначенных только для регулярных выражений и используемых в awk, egrep или lex.This distinguishes it from faster, but more limited, pure regular expression Deterministic Finite Automaton (DFA) engines such as those found in awk, egrep, or lex. Это также отличает его от типовых, но более медленных POSIX NFA-машин.This also distinguishes it from standardized, but slower, POSIX NFAs. В следующем разделе представлено описание трех типов обработчиков регулярных выражений и объясняется причина реализации регулярных выражений на платформе .NET с помощью классического механизма NFA.The following section describes the three types of regular expression engines, and explains why regular expressions in the .NET Framework are implemented by using a traditional NFA engine.

Преимущества NFA-машиныBenefits of the NFA Engine

Когда DFA-машины выполняют сопоставление шаблонов, порядок обработки определяется входной строкой.When DFA engines perform pattern matching, their processing order is driven by the input string. Машина начинает обработку с начала входной строки и продолжает последовательную обработку для определения соответствия следующего символа шаблону регулярного выражения.The engine begins at the beginning of the input string and proceeds sequentially to determine whether the next character matches the regular expression pattern. Их можно обнаружить как соответствия максимальной длины.They can guarantee to match the longest string possible. Так как DFA-машины не проверяют один и тот же знак дважды, они не поддерживают поиск с возвратом.Because they never test the same character twice, DFA engines do not support backtracking. Но поскольку DFA-машина поддерживает только ограниченный режим работы, она не способна выполнять поиск соответствий по шаблону с обратными ссылками. Кроме того, она не создает явные выражения и не способна выделять части выражения.However, because a DFA engine contains only finite state, it cannot match a pattern with backreferences, and because it does not construct an explicit expansion, it cannot capture subexpressions.

В отличие от DFA-машин обычные NFA-машины выполняют поиск соответствий по шаблонам, и порядок обработки определяется шаблоном регулярных выражений.Unlike DFA engines, when traditional NFA engines perform pattern matching, their processing order is driven by the regular expression pattern. По мере обработки определенного элемента языка машина использует жадное сопоставление: она выполняет сопоставление как можно большей части входной строки.As it processes a particular language element, the engine uses greedy matching; that is, it matches as much of the input string as it possibly can. И кроме того, она сохраняет свое состояние после успешного поиска соответствия части выражения.But it also saves its state after successfully matching a subexpression. Если поиск соответствия в конечном счете завершился с ошибкой, машина может вернуться в сохраненное состояние, поэтому она может попытаться найти дополнительные соответствия.If a match eventually fails, the engine can return to a saved state so it can try additional matches. Такой процесс, при котором успешно найденное соответствие части выражения "откладывается" для поиска соответствий с последующими языковыми элементами в регулярном выражении, называется поиском с возвратом.This process of abandoning a successful subexpression match so that later language elements in the regular expression can also match is known as backtracking. NFA-машины используют поиск с возвратом, проверяя все возможные расширения регулярного выражения в определенном порядке и принимая первое соответствие.NFA engines use backtracking to test all possible expansions of a regular expression in a specific order and accept the first match. Поскольку обычная NFA-машина создает определенное расширение регулярного выражения для успешного сопоставления, она способна находить соответствия для частей выражений и обратных ссылок.Because a traditional NFA engine constructs a specific expansion of the regular expression for a successful match, it can capture subexpression matches and matching backreferences. Но так как обычная NFA-машина выполняет поиск с возвратом, она может анализировать одно и то же состояние несколько раз, если к нему ведут несколько разных путей.However, because a traditional NFA backtracks, it can visit the same state multiple times if it arrives at the state over different paths. В результате в наихудшем случае работа может замедляться по экспоненте.As a result, it can run exponentially slowly in the worst case. Так как обычная NFA-машина принимает первое найденное соответствие, другие (возможно, более длинные) соответствия могут остаться необнаруженными.Because a traditional NFA engine accepts the first match it finds, it can also leave other (possibly longer) matches undiscovered.

POSIX NFA-машины похожи на обычные NFA-машины, за исключением того, что они продолжают поиск с возвратом до тех пор, пока не будет найдено наиболее длинное совпадение.POSIX NFA engines are like traditional NFA engines, except that they continue to backtrack until they can guarantee that they have found the longest match possible. В результате POSIX NFA-машина работает медленнее обычной NFA-машины, и при использовании POSIX NFA-машины, изменив порядок поиска с возвратом, невозможно задать предпочтение короткому совпадению вместо более длинного.As a result, a POSIX NFA engine is slower than a traditional NFA engine, and when you use a POSIX NFA engine, you cannot favor a shorter match over a longer one by changing the order of the backtracking search.

Программисты предпочитают обычные NFA-машины, поскольку они превосходят по возможностям управления строковыми соответствиями обычные DFA-машины или POSIX NFA-машины.Traditional NFA engines are favored by programmers because they offer greater control over string matching than either DFA or POSIX NFA engines. Несмотря на то, что в наихудшем случае быстродействие NFA-машин снижается, ими можно управлять так, что поиск соответствий будет проходить по линейному или полиномиальному времени. Добиться этого можно с помощью шаблонов, уменьшающих неоднозначности и ограничивающих количество возвратов.Although, in the worst case, they can run slowly, you can steer them to find matches in linear or polynomial time by using patterns that reduce ambiguities and limit backtracking. Другими словами, несмотря на то, что NFA-машины жертвуют производительностью в обмен на мощность и гибкость, в большинстве случаев они обеспечивают хорошую (или хотя бы приемлемую) производительность, если регулярное выражение грамотно написано и позволяет избежать ситуаций, при которых производительность поиска с возвратом снижается экспоненциально.In other words, although NFA engines trade performance for power and flexibility, in most cases they offer good to acceptable performance if a regular expression is well-written and avoids cases in which backtracking degrades performance exponentially.

Примечание

Сведения о том, как чрезмерное использование поиска с возвратом влияет на производительность и как избегать его в регулярных выражениях, см. в статье Поиск с возвратом в регулярных выражениях.For information about the performance penalty caused by excessive backtracking and ways to craft a regular expression to work around them, see Backtracking.

Возможности обработчика .NET Framework.NET Framework Engine Capabilities

Чтобы использовать все сильные стороны классического механизма NFA, в обработчик регулярных выражений для платформы .NET включен полный набор конструкций, позволяющий программистам управлять процессом поиска с возвратом.To take advantage of the benefits of a traditional NFA engine, the .NET Framework regular expression engine includes a complete set of constructs to enable programmers to steer the backtracking engine. Эти структуры можно использовать для ускорения поиска или для выбора предпочтительных расширений.These constructs can be used to find matches faster or to favor specific expansions over others.

Ниже представлены другие возможности обработчика регулярных выражений .NET Framework.Other features of the .NET Framework regular expression engine include the following:

  • Ленивые квантификаторы: ??, *?, +?, {n,m}?.Lazy quantifiers: ??, *?, +?, {n,m}?. В первую очередь они указывают обработчику на необходимость ведения поиска минимального числа повторений.These constructs tell the backtracking engine to search the minimum number of repetitions first. Обычные "жадные" квантификаторы, наоборот, пытаются сначала найти наибольшее число повторений.In contrast, ordinary greedy quantifiers try to match the maximum number of repetitions first. В следующем примере кода демонстрируется различие между двумя квантификаторами.The following example illustrates the difference between the two. Регулярное выражение соответствует предложению, оканчивающемуся на число, и захваченная группа должна извлечь это число.A regular expression matches a sentence that ends in a number, and a capturing group is intended to extract that number. Регулярное выражение .+(\d+)\. содержит жадный квантификатор .+, под влиянием которого обработчик регулярных выражений захватывает только последнюю цифру числа.The regular expression .+(\d+)\. includes the greedy quantifier .+, which causes the regular expression engine to capture only the last digit of the number. И наоборот, регулярное выражение .+?(\d+)\. содержит ленивый квантификатор .+?, под влиянием которого обработчик регулярных выражений захватывает все число.In contrast, the regular expression .+?(\d+)\. includes the lazy quantifier .+?, which causes the regular expression engine to capture the entire number.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string greedyPattern = @".+(\d+)\.";
          string lazyPattern = @".+?(\d+)\.";
          string input = "This sentence ends with the number 107325.";
          Match match;
          
          // Match using greedy quantifier .+.
          match = Regex.Match(input, greedyPattern);
          if (match.Success)
             Console.WriteLine("Number at end of sentence (greedy): {0}", 
                               match.Groups[1].Value);
          else
             Console.WriteLine("{0} finds no match.", greedyPattern);
                 
          // Match using lazy quantifier .+?.
          match = Regex.Match(input, lazyPattern);
          if (match.Success)
             Console.WriteLine("Number at end of sentence (lazy): {0}", 
                               match.Groups[1].Value);
          else
             Console.WriteLine("{0} finds no match.", lazyPattern);
       }
    }
    // The example displays the following output:
    //       Number at end of sentence (greedy): 5
    //       Number at end of sentence (lazy): 107325
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim greedyPattern As String = ".+(\d+)\."
          Dim lazyPattern As String = ".+?(\d+)\."
          Dim input As String = "This sentence ends with the number 107325."
          Dim match As Match
          
          ' Match using greedy quantifier .+.
          match = Regex.Match(input, greedyPattern)
          If match.Success Then
             Console.WriteLine("Number at end of sentence (greedy): {0}", 
                               match.Groups(1).Value)
          Else
             Console.WriteLine("{0} finds no match.", greedyPattern)
          End If
                 
          ' Match using lazy quantifier .+?.
          match = Regex.Match(input, lazyPattern)
          If match.Success Then
             Console.WriteLine("Number at end of sentence (lazy): {0}", 
                               match.Groups(1).Value)
          Else
             Console.WriteLine("{0} finds no match.", lazyPattern)
          End If
       End Sub
    End Module
    ' The example displays the following output:
    '       Number at end of sentence (greedy): 5
    '       Number at end of sentence (lazy): 107325
    

    Описания "жадной" и "ленивой" версий этого регулярного выражения представлены в следующей таблице.The greedy and lazy versions of this regular expression are defined as shown in the following table:

    ШаблонPattern ОПИСАНИЕDescription
    .+ ("жадный" квантификатор).+ (greedy quantifier) Соответствие как минимум одному вхождению любого символа.Match at least one occurrence of any character. Это дает обработчику регулярных выражений команду на сопоставление всей строки и выполнение поиска с возвратом, который требуется для сопоставления оставшейся части шаблона.This causes the regular expression engine to match the entire string, and then to backtrack as needed to match the remainder of the pattern.
    .+? ("ленивый" квантификатор).+? (lazy quantifier) Соответствие как минимум одному вхождению любого символа, но как можно меньшему количеству.Match at least one occurrence of any character, but match as few as possible.
    (\d+) Соответствие как минимум одной цифре и назначение ее для первой захваченной группы.Match at least one numeric character, and assign it to the first capturing group.
    \. Сопоставляется точка.Match a period.

    Дополнительные сведения о ленивых квантификаторах см. в статье о квантификаторах в регулярных выражениях.For more information about lazy quantifiers, see Quantifiers.

  • Положительный поиск вперед: (?=часть_выражения).Positive lookahead: (?=subexpression). Эта функция позволяет обработчику с поиском с возвратом возвращаться в начальное место в тексте после сопоставления части выражения.This feature allows the backtracking engine to return to the same spot in the text after matching a subexpression. Эта функция полезна при осуществлении поиска с одной позиции с помощью нескольких шаблонов.It is useful for searching throughout the text by verifying multiple patterns that start from the same position. Она также позволяет обработчику проверять существование части строки в конце соответствия, не включая при этом часть строки в сопоставленный текст.It also allows the engine to verify that a substring exists at the end of the match without including the substring in the matched text. В следующем примере используется положительный поиск вперед для извлечения слов в предложении, после которых нет знаков препинания.The following example uses positive lookahead to extract the words in a sentence that are not followed by punctuation symbols.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string pattern = @"\b[A-Z]+\b(?=\P{P})";
          string input = "If so, what comes next?";
          foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
             Console.WriteLine(match.Value);
       }
    }
    // The example displays the following output:
    //       If
    //       what
    //       comes
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim pattern As String = "\b[A-Z]+\b(?=\P{P})"
          Dim input As String = "If so, what comes next?"
          For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
             Console.WriteLine(match.Value)
          Next   
       End Sub
    End Module
    ' The example displays the following output:
    '       If
    '       what
    '       comes
    

    Определение регулярного выражения \b[A-Z]+\b(?=\P{P}) показано в таблице ниже.The regular expression \b[A-Z]+\b(?=\P{P}) is defined as shown in the following table.

    ШаблонPattern ОПИСАНИЕDescription
    \b Совпадение должно начинаться на границе слова.Begin the match at a word boundary.
    [A-Z]+ Совпадение с любым символом один или более раз.Match any alphabetic character one or more times. Поскольку метод Regex.Matches вызывается с параметром RegexOptions.IgnoreCase, при сравнении не учитывается регистр символов.Because the Regex.Matches method is called with the RegexOptions.IgnoreCase option, the comparison is case-insensitive.
    \b Совпадение должно заканчиваться на границе слова.End the match at a word boundary.
    (?=\P{P}) Поиск для определения, является ли следующий символ знаком препинания.Look ahead to determine whether the next character is a punctuation symbol. Если не является, соответствие считается успешным.If it is not, the match succeeds.

    Дополнительные сведения об утверждениях положительного поиска вперед см. в статье Конструкции группировки в регулярных выражениях.For more information about positive lookahead assertions, see Grouping Constructs.

  • Отрицательный поиск вперед: (?!часть_выражения).Negative lookahead: (?!subexpression). Сопоставление выражения выполняется только в том случае, когда не было обнаружено соответствия части выражения.This feature adds the ability to match an expression only if a subexpression fails to match. Это существенно упрощает поиск, поскольку часто бывает проще описать выражения, не соответствующие правилу, чем само правило.This is particularly powerful for pruning a search, because it is often simpler to provide an expression for a case that should be eliminated than an expression for cases that must be included. Например, трудно написать выражение для поиска слов, которые не начинаются с "non".For example, it is difficult to write an expression for words that do not begin with "non". В следующем примере для их исключения используется отрицательный поиск.The following example uses negative lookahead to exclude them.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string pattern = @"\b(?!non)\w+\b";
          string input = "Nonsense is not always non-functional.";
          foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
             Console.WriteLine(match.Value);
       }
    }
    // The example displays the following output:
    //       is
    //       not
    //       always
    //       functional
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim pattern As String = "\b(?!non)\w+\b"
          Dim input As String = "Nonsense is not always non-functional."
          For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
             Console.WriteLine(match.Value)
          Next
       End Sub
    End Module
    ' The example displays the following output:
    '       is
    '       not
    '       always
    '       functional
    

    Шаблон регулярного выражения \b(?!non)\w+\b определяется, как показано в следующей таблице.The regular expression pattern \b(?!non)\w+\b is defined as shown in the following table.

    ШаблонPattern ОПИСАНИЕDescription
    \b Совпадение должно начинаться на границе слова.Begin the match at a word boundary.
    (?!non) Поиск для определения, не начинается ли текущая строка с "non".Look ahead to ensure that the current string does not begin with "non". Если начинается, соответствие считается неудачным.If it does, the match fails.
    (\w+) Совпадение с одним или несколькими символами слова.Match one or more word characters.
    \b Совпадение должно заканчиваться на границе слова.End the match at a word boundary.

    Дополнительные сведения об утверждениях отрицательного поиска вперед см. в статье Конструкции группировки в регулярных выражениях.For more information about negative lookahead assertions, see Grouping Constructs.

  • Условная оценка: (?(выражение)да|нет) and (?(имя)да|нет), где выражение — сопоставляемая часть выражения, имя — имя группы захвата, да — сопоставляемая строка, если выражение найдено в тексте или имя является допустимой непустой захваченной группой, а нет — сопоставляемая строка, если выражение не найдено или имя не является допустимой непустой захваченной группой.Conditional evaluation: (?(expression)yes|no) and (?(name)yes|no), where expression is a subexpression to match, name is the name of a capturing group, yes is the string to match if expression is matched or name is a valid, non-empty captured group, and no is the subexpression to match if expression is not matched or name is not a valid, non-empty captured group. Эта функция позволяет обработчику вести поиск с использованием нескольких альтернативных шаблонов в зависимости от результатов сопоставлений предыдущей части выражения или утверждения нулевой ширины.This feature allows the engine to search by using more than one alternate pattern, depending on the result of a previous subexpression match or the result of a zero-width assertion. Это более действенный вид обратной ссылки, позволяющий, например, искать соответствия части выражения в зависимости от соответствия предыдущей части выражения.This allows a more powerful form of backreference that permits, for example, matching a subexpression based on whether a previous subexpression was matched. Регулярное выражение в следующем примере соответствует абзацам, предназначенным для общего и внутреннего использования.The regular expression in the following example matches paragraphs that are intended for both public and internal use. Абзацы, предназначенные только для внутреннего использования, начинаются с тега <PRIVATE>.Paragraphs intended only for internal use begin with a <PRIVATE> tag. Шаблон регулярного выражения ^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$ использует условную оценку для назначения содержимого абзацев, предназначенных для общего и внутреннего использования, для отдельных захваченных групп.The regular expression pattern ^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$ uses conditional evaluation to assign the contents of paragraphs intended for public and for internal use to separate capturing groups. Поэтому эти абзацы можно обработать по-разному.These paragraphs can then be handled differently.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string input = "<PRIVATE> This is not for public consumption." + Environment.NewLine + 
                         "But this is for public consumption." + Environment.NewLine + 
                         "<PRIVATE> Again, this is confidential.\n";  
          string pattern = @"^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$";
          string publicDocument = null, privateDocument = null;
          
          foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Multiline))
          {
             if (match.Groups[1].Success) {
                privateDocument += match.Groups[1].Value + "\n";
             }
             else {
                publicDocument += match.Groups[3].Value + "\n";   
                privateDocument += match.Groups[3].Value + "\n";
             }  
          }
    
          Console.WriteLine("Private Document:");
          Console.WriteLine(privateDocument);
          Console.WriteLine("Public Document:");
          Console.WriteLine(publicDocument);
       }
    }
    // The example displays the following output:
    //    Private Document:
    //    This is not for public consumption.
    //    But this is for public consumption.
    //    Again, this is confidential.
    //    
    //    Public Document:
    //    But this is for public consumption.
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim input As String = "<PRIVATE> This is not for public consumption." + vbCrLf + _
                                "But this is for public consumption." + vbCrLf + _
                                "<PRIVATE> Again, this is confidential." + vbCrLf
          Dim pattern As String = "^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$"
          Dim publicDocument As String = Nothing
          Dim privateDocument As String = Nothing
          
          For Each match As Match In Regex.Matches(input, pattern, RegexOptions.Multiline)
             If match.Groups(1).Success Then
                privateDocument += match.Groups(1).Value + vbCrLf
             Else
                publicDocument += match.Groups(3).Value + vbCrLf   
                privateDocument += match.Groups(3).Value + vbCrLf
             End If  
          Next
    
          Console.WriteLine("Private Document:")
          Console.WriteLine(privateDocument)
          Console.WriteLine("Public Document:")
          Console.WriteLine(publicDocument)
       End Sub
    End Module
    ' The example displays the following output:
    '    Private Document:
    '    This is not for public consumption.
    '    But this is for public consumption.
    '    Again, this is confidential.
    '    
    '    Public Document:
    '    But this is for public consumption.
    

    Шаблон регулярного выражения определяется, как показано в следующей таблице.The regular expression pattern is defined as shown in the following table.

    ШаблонPattern ОПИСАНИЕDescription
    ^ Начало совпадения в начале строки.Begin the match at the beginning of a line.
    (?<Pvt>\<PRIVATE\>\s)? Соответствует вхождениям в количестве 0 или 1 строки <PRIVATE> за которым следует символ пробела.Match zero or one occurrence of the string <PRIVATE> followed by a white-space character. Назначение соответствия для захваченной группы с именем Pvt.Assign the match to a capturing group named Pvt.
    (?(Pvt)((\w+\p{P}?\s)+) Если захваченная группа Pvt существует, сопоставление одного или нескольких вхождений одного или нескольких вхождений символов слов, за которыми следует 0 или 1 пунктуационный разделитель с последующим любым пробелом.If the Pvt capturing group exists, match one or more occurrences of one or more word characters followed by zero or one punctuation separator followed by a white-space character. Назначение части строки первой захваченной группе.Assign the substring to the first capturing group.
    |((\w+\p{P}?\s)+)) Если захваченная группа Pvt не существует, сопоставление одного или нескольких вхождений одного или нескольких вхождений символов слов, за которыми следует 0 или 1 пунктуационный разделитель с последующим любым пробелом.If the Pvt capturing group does not exist, match one or more occurrences of one or more word characters followed by zero or one punctuation separator followed by a white-space character. Назначение части строки третьей захваченной группе.Assign the substring to the third capturing group.
    \r?$ Соответствует концу строки.Match the end of a line or the end of the string.

    Дополнительные сведения об условной оценке см. в статье Конструкции изменения в регулярных выражениях.For more information about conditional evaluation, see Alternation Constructs.

  • Сбалансированные определения группы: (?<имя1-имя2> часть_выражения).Balancing group definitions: (?<name1-name2> subexpression). Эта функция позволяет обработчику регулярных выражений отслеживать вложенные конструкции, такие как скобки или открывающие и закрывающие круглые скобки.This feature allows the regular expression engine to keep track of nested constructs such as parentheses or opening and closing brackets. Пример см. в статье Конструкции группировки в регулярных выражениях.For an example, see Grouping Constructs.

  • Невозвращающиеся ("жадные") части выражения: (?>часть_выражения).Nonbacktracking subexpressions (also known as greedy subexpressions): (?>subexpression). Эта функция обеспечивает для подвыражения верность только первого соответствия, как будто выражение запускалось независимо от содержащего его выражения.This feature allows the backtracking engine to guarantee that a subexpression matches only the first match found for that subexpression, as if the expression were running independent of its containing expression. Без этой конструкции поиск в большом выражении с использованием поиска с возвратом может изменить поведение части выражения.If you do not use this construct, backtracking searches from the larger expression can change the behavior of a subexpression. Например, регулярное выражение (a+)\w соответствует одному или нескольким символам "a" наряду с буквой, после которой идет последовательность символов "a", и назначает последовательность символов "a" для первой захваченной группы. Однако если последний символ входной строки также является символом "a", он соответствует элементу языка \w и не входит в захваченную группу.For example, the regular expression (a+)\w matches one or more "a" characters, along with a word character that follows the sequence of "a" characters, and assigns the sequence of "a" characters to the first capturing group, However, if the final character of the input string is also an "a", it is matched by the \w language element and is not included in the captured group.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string[] inputs = { "aaaaa", "aaaaab" };
          string backtrackingPattern = @"(a+)\w";
          Match match;
    
          foreach (string input in inputs) {
             Console.WriteLine("Input: {0}", input);
             match = Regex.Match(input, backtrackingPattern);
             Console.WriteLine("   Pattern: {0}", backtrackingPattern);
             if (match.Success) { 
                Console.WriteLine("      Match: {0}", match.Value);
                Console.WriteLine("      Group 1: {0}", match.Groups[1].Value);
             }
             else {
                Console.WriteLine("      Match failed.");
             }   
          }
          Console.WriteLine();            
       }
    }
    // The example displays the following output:
    //       Input: aaaaa
    //          Pattern: (a+)\w
    //             Match: aaaaa
    //             Group 1: aaaa
    //       Input: aaaaab
    //          Pattern: (a+)\w
    //             Match: aaaaab
    //             Group 1: aaaaa
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim inputs() As String = { "aaaaa", "aaaaab" }
          Dim backtrackingPattern As String = "(a+)\w"
          Dim match As Match
    
          For Each input As String In inputs
             Console.WriteLine("Input: {0}", input)
             match = Regex.Match(input, backtrackingPattern)
             Console.WriteLine("   Pattern: {0}", backtrackingPattern)
             If match.Success Then 
                Console.WriteLine("      Match: {0}", match.Value)
                Console.WriteLine("      Group 1: {0}", match.Groups(1).Value)
             Else 
                Console.WriteLine("      Match failed.")
             End If   
          Next
          Console.WriteLine()            
       End Sub
    End Module
    ' The example displays the following output:
    '       Input: aaaaa
    '          Pattern: (a+)\w
    '             Match: aaaaa
    '             Group 1: aaaa
    '       Input: aaaaab
    '          Pattern: (a+)\w
    '             Match: aaaaab
    '             Group 1: aaaaa
    

    Регулярное выражение ((?>a+))\w препятствует такому поведению.The regular expression ((?>a+))\w prevents this behavior. Поскольку все последовательные символы "a" имеют соответствия без поиска с возвратом, первая захваченная группа содержит все последовательные символы "a".Because all consecutive "a" characters are matched without backtracking, the first capturing group includes all consecutive "a" characters. Если после символов "a" не следует ни один символ, отличный от "a", соответствие считается неудачным.If the "a" characters are not followed by at least one more character other than "a", the match fails.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string[] inputs = { "aaaaa", "aaaaab" };
          string nonbacktrackingPattern = @"((?>a+))\w";
          Match match;
    
          foreach (string input in inputs) {
             Console.WriteLine("Input: {0}", input);
             match = Regex.Match(input, nonbacktrackingPattern);
             Console.WriteLine("   Pattern: {0}", nonbacktrackingPattern);
             if (match.Success) { 
                Console.WriteLine("      Match: {0}", match.Value);
                Console.WriteLine("      Group 1: {0}", match.Groups[1].Value);
             }
             else {
                Console.WriteLine("      Match failed.");
             }   
          }
          Console.WriteLine();            
       }
    }
    // The example displays the following output:
    //       Input: aaaaa
    //          Pattern: ((?>a+))\w
    //             Match failed.
    //       Input: aaaaab
    //          Pattern: ((?>a+))\w
    //             Match: aaaaab
    //             Group 1: aaaaa
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim inputs() As String = { "aaaaa", "aaaaab" }
          Dim nonbacktrackingPattern As String = "((?>a+))\w"
          Dim match As Match
    
          For Each input As String In inputs
             Console.WriteLine("Input: {0}", input)
             match = Regex.Match(input, nonbacktrackingPattern)
             Console.WriteLine("   Pattern: {0}", nonbacktrackingPattern)
             If match.Success Then 
                Console.WriteLine("      Match: {0}", match.Value)
                Console.WriteLine("      Group 1: {0}", match.Groups(1).Value)
             Else 
                Console.WriteLine("      Match failed.")
             End If   
          Next
          Console.WriteLine()            
       End Sub
    End Module
    ' The example displays the following output:
    '       Input: aaaaa
    '          Pattern: ((?>a+))\w
    '             Match failed.
    '       Input: aaaaab
    '          Pattern: ((?>a+))\w
    '             Match: aaaaab
    '             Group 1: aaaaa
    

    Дополнительные сведения о невозвращающихся частях выражений см. в статье Конструкции группировки в регулярных выражениях.For more information about nonbacktracking subexpressions, see Grouping Constructs.

  • Поиск совпадений справа налево, для применения которого нужно передать параметр RegexOptions.RightToLeft в конструктор класса Regex или в статический метод сопоставления экземпляров.Right-to-left matching, which is specified by supplying the RegexOptions.RightToLeft option to a Regex class constructor or static instance matching method. Эта функция полезна при поиске справа налево вместо обычного поиска слева направо, а также бывает более эффективно начинать поиск с правой части шаблона, а не с левой.This feature is useful when searching from right to left instead of from left to right, or in cases where it is more efficient to begin a match at the right part of the pattern instead of the left. Как показано в примере ниже, использование поиска соответствий справа налево может изменить поведение жадных квантификаторов.As the following example illustrates, using right-to-left matching can change the behavior of greedy quantifiers. В примере выполняется два поисковых запроса предложения, оканчивающегося на число.The example conducts two searches for a sentence that ends in a number. При поиске слева направо с использованием жадного квантификатора + имеется соответствие одной из шести цифр в предложении, тогда как при поиске справа налево — всем шести цифрам.The left-to-right search that uses the greedy quantifier + matches one of the six digits in the sentence, whereas the right-to-left search matches all six digits. Описание шаблона регулярного выражения см. в примере с ленивыми квантификаторами ранее в этом разделе.For an description of the regular expression pattern, see the example that illustrates lazy quantifiers earlier in this section.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string greedyPattern = @".+(\d+)\.";
          string input = "This sentence ends with the number 107325.";
          Match match;
          
          // Match from left-to-right using lazy quantifier .+?.
          match = Regex.Match(input, greedyPattern);
          if (match.Success)
             Console.WriteLine("Number at end of sentence (left-to-right): {0}", 
                               match.Groups[1].Value);
          else
             Console.WriteLine("{0} finds no match.", greedyPattern);
          
          // Match from right-to-left using greedy quantifier .+.
          match = Regex.Match(input, greedyPattern, RegexOptions.RightToLeft);
          if (match.Success)
             Console.WriteLine("Number at end of sentence (right-to-left): {0}", 
                               match.Groups[1].Value);
          else
             Console.WriteLine("{0} finds no match.", greedyPattern);
       }
    }
    // The example displays the following output:
    //       Number at end of sentence (left-to-right): 5
    //       Number at end of sentence (right-to-left): 107325
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim greedyPattern As String = ".+(\d+)\."
          Dim input As String = "This sentence ends with the number 107325."
          Dim match As Match
          
          ' Match from left-to-right using lazy quantifier .+?.
          match = Regex.Match(input, greedyPattern)
          If match.Success Then
             Console.WriteLine("Number at end of sentence (left-to-right): {0}", 
                               match.Groups(1).Value)
          Else
             Console.WriteLine("{0} finds no match.", greedyPattern)
          End If
          
          ' Match from right-to-left using greedy quantifier .+.
          match = Regex.Match(input, greedyPattern, RegexOptions.RightToLeft)
          If match.Success Then
             Console.WriteLine("Number at end of sentence (right-to-left): {0}", 
                               match.Groups(1).Value)
          Else
             Console.WriteLine("{0} finds no match.", greedyPattern)
          End If
       End Sub
    End Module
    ' The example displays the following output:
    '       Number at end of sentence (left-to-right): 5
    '       Number at end of sentence (right-to-left): 107325
    

    Дополнительные сведения о поиске соответствий справа налево см. в разделе Параметры регулярных выражений.For more information about right-to-left matching, see Regular Expression Options.

  • Положительный и отрицательный поиск назад: (?<=часть_выражения) для положительного поиска назад и (?<!часть_выражения) для отрицательного просмотра назад.Positive and negative lookbehind: (?<=subexpression) for positive lookbehind, and (?<!subexpression) for negative lookbehind. Эта функция аналогична поиску вперед, рассмотренному ранее в этом разделе.This feature is similar to lookahead, which is discussed earlier in this topic. Поскольку обработчик регулярных выражений позволяет выполнять поиск справа налево, к регулярным выражениям можно применять поиск назад без каких-либо ограничений.Because the regular expression engine allows complete right-to-left matching, regular expressions allow unrestricted lookbehinds. С помощью положительного и отрицательного поиска назад также можно избегать вложенных квантификаторов, когда вложенная часть выражения является супермножеством внешнего выражения.Positive and negative lookbehind can also be used to avoid nesting quantifiers when the nested subexpression is a superset of an outer expression. Регулярные выражения с такими вложенными квантификаторами часто являются причиной низкой производительности.Regular expressions with such nested quantifiers often offer poor performance. В следующем примере выполняется проверка, начинается ли и оканчивается ли строка с буквы или цифры, а также является ли любой другой символ в строке символом большего подмножества.For example, the following example verifies that a string begins and ends with an alphanumeric character, and that any other character in the string is one of a larger subset. В результате формируется часть регулярного выражения для проверки адресов электронной почты. Дополнительные сведения см. в статье Руководство. Проверка строк на соответствие формату электронной почты.It forms a portion of the regular expression used to validate email addresses; for more information, see How to: Verify that Strings Are in Valid Email Format.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
          string[] inputs = { "jack.sprat", "dog#", "dog#1", "me.myself", 
                              "me.myself!" };
          string pattern = @"^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$";
          foreach (string input in inputs) {
             if (Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase))
                Console.WriteLine("{0}: Valid", input);
             else
                Console.WriteLine("{0}: Invalid", input);
          }
       }
    }
    // The example displays the following output:
    //       jack.sprat: Valid
    //       dog#: Invalid
    //       dog#1: Valid
    //       me.myself: Valid
    //       me.myself!: Invalid
    
    Imports System.Text.RegularExpressions
    
    Module Example
       Public Sub Main()
          Dim inputs() As String = { "jack.sprat", "dog#", "dog#1", "me.myself", 
                                     "me.myself!" }
          Dim pattern As String = "^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$"
          For Each input As String In inputs
             If Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase) Then
                Console.WriteLine("{0}: Valid", input)
             Else
                Console.WriteLine("{0}: Invalid", input)
             End If   
          Next
       End Sub
    End Module
    ' The example displays the following output:
    '       jack.sprat: Valid
    '       dog#: Invalid
    '       dog#1: Valid
    '       me.myself: Valid
    '       me.myself!: Invalid
    

    Определение регулярного выражения ^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$ показано в таблице ниже.The regular expression ^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$ is defined as shown in the following table.

    ШаблонPattern ОПИСАНИЕDescription
    ^ Начало совпадения в начале строки.Begin the match at the beginning of the string.
    [A-Z0-9] Соответствие любому алфавитно-цифровому символу.Match any numeric or alphanumeric character. (При сравнении регистр не учитывается.)(The comparison is case-insensitive.)
    ([-!#$%&'.*+/=?^`{}|~\w])*Соответствие нулю или нескольким вхождениям любого символа слова или любого символа из следующего набора: -, !, #, $, %, &, ', ., *, +, /, =, ?, ^, `, {, }, |, или ~.([-!#$%&'.*+/=?^`{}|~\w])* Match zero or more occurrences of any word character, or any of the following characters: -, !, #, $, %, &, ', ., *, +, /, =, ?, ^, `, {, }, |, or ~.
    (?<=[A-Z0-9]) Поиск назад предыдущего символа, который должен являться числом или буквенно-цифровым символом.Look behind to the previous character, which must be numeric or alphanumeric. (При сравнении регистр не учитывается.)(The comparison is case-insensitive.)
    $ Совпадение должно заканчиваться в конце строки.End the match at the end of the string.

    Дополнительные сведения о положительном и отрицательном поиске назад см. в статье Конструкции группировки в регулярных выражениях.For more information about positive and negative lookbehind, see Grouping Constructs.

ЗаголовокTitle ОПИСАНИЕDescription
Поиск с возвратомBacktracking Сведения об использовании поиска с возвратом для поиска альтернативных соответствий.Provides information about how regular expression backtracking branches to find alternative matches.
Компиляция и многократное использованиеCompilation and Reuse Сведения о компиляции и многократном использовании регулярных выражений для повышения производительности.Provides information about compiling and reusing regular expressions to increase performance.
ПотокобезопасностьThread Safety Сведения о потокобезопасности регулярных выражений и времени синхронизации доступа к объектам регулярных выражений.Provides information about regular expression thread safety and explains when you should synchronize access to regular expression objects.
Регулярные выражения в .NET Framework.NET Framework Regular Expressions Общие сведения о регулярных выражениях в контексте языка программирования.Provides an overview of the programming language aspect of regular expressions.
Объектная модель регулярных выраженийThe Regular Expression Object Model Сведения об использовании классов регулярных выражений и примеры кода.Provides information and code examples illustrating how to use the regular expression classes.
Примеры регулярных выраженийRegular Expression Examples Примеры кодов, иллюстрирующих использование регулярных выражений в обычных приложениях.Contains code examples that illustrate the use of regular expressions in common applications.
Элементы языка регулярных выражений — краткий справочникRegular Expression Language - Quick Reference Сведения о наборе символов, операторов и конструкций, которые можно использовать для определения регулярных выражений.Provides information about the set of characters, operators, and constructs that you can use to define regular expressions.

СсылкаReference

System.Text.RegularExpressions