Рекомендации по использованию регулярных выражений в .NETBest Practices for Regular Expressions in .NET

Обработчик регулярных выражений в .NET — мощное полнофункциональное средство для обработки текста на основе сопоставления шаблонов, которое не сравнивает текст напрямую.The regular expression engine in .NET is a powerful, full-featured tool that processes text based on pattern matches rather than on comparing and matching literal text. В большинстве случаев сопоставление шаблонов выполняется быстро и эффективно.In most cases, it performs pattern matching rapidly and efficiently. Однако в некоторых случаях обработчик регулярных выражений может работать очень медленно.However, in some cases, the regular expression engine can appear to be very slow. В крайних случаях он даже может перестать отвечать, обрабатывая относительно небольшой объем входной информации в течение часов или даже дней.In extreme cases, it can even appear to stop responding as it processes a relatively small input over the course of hours or even days.

В этом разделе приведены некоторые рекомендации для разработчиков по обеспечению оптимальной производительности регулярных выражений.This topic outlines some of the best practices that developers can adopt to ensure that their regular expressions achieve optimal performance. Он содержит следующие подразделы:It contains the following sections:

Учет источника входных данныхConsider the Input Source

Регулярные выражения могут принимать два типа входных данных: определенные и произвольные.In general, regular expressions can accept two types of input: constrained or unconstrained. Определенные входные данные — это текст, происходящий из известного надежного источника в заранее определенном формате.Constrained input is text that originates from a known or reliable source and follows a predefined format. Произвольные входные данные — это текст, происходящий из ненадежного источника, например от пользователя в Интернете, который может не соответствовать заданному или ожидаемому формату.Unconstrained input is text that originates from an unreliable source, such as a web user, and may not follow a predefined or expected format.

Шаблоны регулярных выражений обычно ориентированы на подходящие входные данные.Regular expression patterns are typically written to match valid input. Это значит, что разработчик анализирует текст, который требуется найти, и составляет шаблон регулярного выражения.That is, developers examine the text that they want to match and then write a regular expression pattern that matches it. Затем разработчик определяет, требуется ли корректировка шаблона или его уточнение, тестируя его с различными подходящими входными данными.Developers then determine whether this pattern requires correction or further elaboration by testing it with multiple valid input items. Когда шаблон соответствует всем возможным подходящим вариантам входных данных, считается, что он готов и его можно включить в выпускаемое приложение.When the pattern matches all presumed valid inputs, it is declared to be production-ready and can be included in a released application. В результате этих действий шаблон регулярного выражения готов для сопоставления с определенными входными данными.This makes a regular expression pattern suitable for matching constrained input. Но он не готов для сопоставления с произвольными входными данными.However, it does not make it suitable for matching unconstrained input.

Для сопоставления с произвольными входными данными шаблон регулярного выражения должен уметь обрабатывать три вида текста:To match unconstrained input, a regular expression must be able to efficiently handle three kinds of text:

  • Текст, соответствующий шаблону регулярного выражения.Text that matches the regular expression pattern.

  • Текст, не соответствующий шаблону регулярного выражения.Text that does not match the regular expression pattern.

  • Текст, почти соответствующий шаблону регулярного выражения.Text that nearly matches the regular expression pattern.

Последний вид текста представляет особую проблему для регулярных выражений, написанных для обработки определенных входных данных.The last text type is especially problematic for a regular expression that has been written to handle constrained input. Если в регулярном выражении также широко используется поиск с возвратом, обработчик регулярных выражений может неожиданно долго (в некоторых случаях часы или дни) обрабатывать, казалось бы, безобидный текст.If that regular expression also relies on extensive backtracking, the regular expression engine can spend an inordinate amount of time (in some cases, many hours or days) processing seemingly innocuous text.

Предупреждение

В следующем примере используется регулярное выражение, которое подвержено избыточному использованию поиска с возвратом и может отклонить допустимые адреса электронной почты.The following example uses a regular expression that is prone to excessive backtracking and that is likely to reject valid email addresses. Использовать его для проверки электронной почты не следует.You should not use it in an email validation routine. Если требуется регулярное выражение, которое проверяет адреса электронной почты, см. раздел Руководство. Проверка строк на соответствие формату электронной почты.If you would like a regular expression that validates email addresses, see How to: Verify that Strings Are in Valid Email Format.

Например, рассмотрим очень часто используемое и при этом весьма проблематичное регулярное выражение для проверки псевдонима адреса электронной почты.For example, consider a very commonly used but extremely problematic regular expression for validating the alias of an email address. Регулярное выражение ^[0-9A-Z]([-.\w]*[0-9A-Z])*$ предназначено для обработки допустимого адреса электронной почты и состоит из алфавитно-цифрового символа, за которым следует ноль или более знаков, которые могут представлять собой алфавитно-цифровые символы, точки или дефисы.The regular expression ^[0-9A-Z]([-.\w]*[0-9A-Z])*$ is written to process what is considered to be a valid email address, which consists of an alphanumeric character, followed by zero or more characters that can be alphanumeric, periods, or hyphens. Регулярное выражение должно оканчиваться алфавитно-цифровым символом.The regular expression must end with an alphanumeric character. Однако, как показывает следующий пример, несмотря на то что это регулярное выражение хорошо обрабатывает подходящие входные данные, оно очень неэффективно при обработке почти подходящих входных данных.However, as the following example shows, although this regular expression handles valid input easily, its performance is very inefficient when it is processing nearly valid input.

using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      Stopwatch sw;    
      string[] addresses = { "AAAAAAAAAAA@contoso.com", 
                             "AAAAAAAAAAaaaaaaaaaa!@contoso.com" };
      // The following regular expression should not actually be used to 
      // validate an email address.
      string pattern = @"^[0-9A-Z]([-.\w]*[0-9A-Z])*$";
      string input; 
      
      foreach (var address in addresses) {
         string mailBox = address.Substring(0, address.IndexOf("@"));       
         int index = 0;
         for (int ctr = mailBox.Length - 1; ctr >= 0; ctr--) {
            index++;

            input = mailBox.Substring(ctr, index); 
            sw = Stopwatch.StartNew();
            Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase);
            sw.Stop();
            if (m.Success)
               Console.WriteLine("{0,2}. Matched '{1,25}' in {2}", 
                                 index, m.Value, sw.Elapsed);
            else                     
               Console.WriteLine("{0,2}. Failed  '{1,25}' in {2}", 
                                 index, input, sw.Elapsed);
         }
         Console.WriteLine();
      }
   }
}

// The example displays output similar to the following:
//     1. Matched '                        A' in 00:00:00.0007122
//     2. Matched '                       AA' in 00:00:00.0000282
//     3. Matched '                      AAA' in 00:00:00.0000042
//     4. Matched '                     AAAA' in 00:00:00.0000038
//     5. Matched '                    AAAAA' in 00:00:00.0000042
//     6. Matched '                   AAAAAA' in 00:00:00.0000042
//     7. Matched '                  AAAAAAA' in 00:00:00.0000042
//     8. Matched '                 AAAAAAAA' in 00:00:00.0000087
//     9. Matched '                AAAAAAAAA' in 00:00:00.0000045
//    10. Matched '               AAAAAAAAAA' in 00:00:00.0000045
//    11. Matched '              AAAAAAAAAAA' in 00:00:00.0000045
//    
//     1. Failed  '                        !' in 00:00:00.0000447
//     2. Failed  '                       a!' in 00:00:00.0000071
//     3. Failed  '                      aa!' in 00:00:00.0000071
//     4. Failed  '                     aaa!' in 00:00:00.0000061
//     5. Failed  '                    aaaa!' in 00:00:00.0000081
//     6. Failed  '                   aaaaa!' in 00:00:00.0000126
//     7. Failed  '                  aaaaaa!' in 00:00:00.0000359
//     8. Failed  '                 aaaaaaa!' in 00:00:00.0000414
//     9. Failed  '                aaaaaaaa!' in 00:00:00.0000758
//    10. Failed  '               aaaaaaaaa!' in 00:00:00.0001462
//    11. Failed  '              aaaaaaaaaa!' in 00:00:00.0002885
//    12. Failed  '             Aaaaaaaaaaa!' in 00:00:00.0005780
//    13. Failed  '            AAaaaaaaaaaa!' in 00:00:00.0011628
//    14. Failed  '           AAAaaaaaaaaaa!' in 00:00:00.0022851
//    15. Failed  '          AAAAaaaaaaaaaa!' in 00:00:00.0045864
//    16. Failed  '         AAAAAaaaaaaaaaa!' in 00:00:00.0093168
//    17. Failed  '        AAAAAAaaaaaaaaaa!' in 00:00:00.0185993
//    18. Failed  '       AAAAAAAaaaaaaaaaa!' in 00:00:00.0366723
//    19. Failed  '      AAAAAAAAaaaaaaaaaa!' in 00:00:00.1370108
//    20. Failed  '     AAAAAAAAAaaaaaaaaaa!' in 00:00:00.1553966
//    21. Failed  '    AAAAAAAAAAaaaaaaaaaa!' in 00:00:00.3223372
Imports System.Diagnostics
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim sw As Stopwatch    
      Dim addresses() As String = { "AAAAAAAAAAA@contoso.com", 
                                 "AAAAAAAAAAaaaaaaaaaa!@contoso.com" }
      ' The following regular expression should not actually be used to 
      ' validate an email address.
      Dim pattern As String = "^[0-9A-Z]([-.\w]*[0-9A-Z])*$"
      Dim input As String 
      
      For Each address In addresses
         Dim mailBox As String = address.Substring(0, address.IndexOf("@"))       
         Dim index As Integer = 0
         For ctr As Integer = mailBox.Length - 1 To 0 Step -1
            index += 1
            input = mailBox.Substring(ctr, index) 
            sw = Stopwatch.StartNew()
            Dim m As Match = Regex.Match(input, pattern, RegexOptions.IgnoreCase)
            sw.Stop()
            if m.Success Then
               Console.WriteLine("{0,2}. Matched '{1,25}' in {2}", 
                                 index, m.Value, sw.Elapsed)
            Else                     
               Console.WriteLine("{0,2}. Failed  '{1,25}' in {2}", 
                                 index, input, sw.Elapsed)
            End If                  
         Next
         Console.WriteLine()
      Next
   End Sub
End Module
' The example displays output similar to the following:
'     1. Matched '                        A' in 00:00:00.0007122
'     2. Matched '                       AA' in 00:00:00.0000282
'     3. Matched '                      AAA' in 00:00:00.0000042
'     4. Matched '                     AAAA' in 00:00:00.0000038
'     5. Matched '                    AAAAA' in 00:00:00.0000042
'     6. Matched '                   AAAAAA' in 00:00:00.0000042
'     7. Matched '                  AAAAAAA' in 00:00:00.0000042
'     8. Matched '                 AAAAAAAA' in 00:00:00.0000087
'     9. Matched '                AAAAAAAAA' in 00:00:00.0000045
'    10. Matched '               AAAAAAAAAA' in 00:00:00.0000045
'    11. Matched '              AAAAAAAAAAA' in 00:00:00.0000045
'    
'     1. Failed  '                        !' in 00:00:00.0000447
'     2. Failed  '                       a!' in 00:00:00.0000071
'     3. Failed  '                      aa!' in 00:00:00.0000071
'     4. Failed  '                     aaa!' in 00:00:00.0000061
'     5. Failed  '                    aaaa!' in 00:00:00.0000081
'     6. Failed  '                   aaaaa!' in 00:00:00.0000126
'     7. Failed  '                  aaaaaa!' in 00:00:00.0000359
'     8. Failed  '                 aaaaaaa!' in 00:00:00.0000414
'     9. Failed  '                aaaaaaaa!' in 00:00:00.0000758
'    10. Failed  '               aaaaaaaaa!' in 00:00:00.0001462
'    11. Failed  '              aaaaaaaaaa!' in 00:00:00.0002885
'    12. Failed  '             Aaaaaaaaaaa!' in 00:00:00.0005780
'    13. Failed  '            AAaaaaaaaaaa!' in 00:00:00.0011628
'    14. Failed  '           AAAaaaaaaaaaa!' in 00:00:00.0022851
'    15. Failed  '          AAAAaaaaaaaaaa!' in 00:00:00.0045864
'    16. Failed  '         AAAAAaaaaaaaaaa!' in 00:00:00.0093168
'    17. Failed  '        AAAAAAaaaaaaaaaa!' in 00:00:00.0185993
'    18. Failed  '       AAAAAAAaaaaaaaaaa!' in 00:00:00.0366723
'    19. Failed  '      AAAAAAAAaaaaaaaaaa!' in 00:00:00.1370108
'    20. Failed  '     AAAAAAAAAaaaaaaaaaa!' in 00:00:00.1553966
'    21. Failed  '    AAAAAAAAAAaaaaaaaaaa!' in 00:00:00.3223372

Как видно по выводу примера, обработчик регулярных выражений обрабатывает подходящие псевдонимы электронной почты различной длины практически за одинаковое время.As the output from the example shows, the regular expression engine processes the valid email alias in about the same time interval regardless of its length. С другой стороны, если почти подходящий адрес электронной почты содержит более 5 символов, время обработки удваивается для каждого дополнительного символа в строке.On the other hand, when the nearly valid email address has more than five characters, processing time approximately doubles for each additional character in the string. Это значит, что почти подходящая строка из 28 символов будет обрабатываться более часа, а на обработку почти подходящей строки из 33 символов уйдет около дня.This means that a nearly valid 28-character string would take over an hour to process, and a nearly valid 33-character string would take nearly a day to process.

Поскольку это регулярное выражение ориентировано только на формат подходящих входных данных, оно плохо работает со входными данными, не соответствующими шаблону.Because this regular expression was developed solely by considering the format of input to be matched, it fails to take account of input that does not match the pattern. А это может привести к тому, что произвольные входные данные, почти соответствующие шаблону регулярного выражения, могут существенно снизить производительность.This, in turn, can allow unconstrained input that nearly matches the regular expression pattern to significantly degrade performance.

Чтобы устранить эту проблему, можно выполнить одно из следующих действий.To solve this problem, you can do the following:

  • При создании шаблона необходимо учитывать, как поиск с возвратом может повлиять на производительность обработчика регулярных выражений, особенно если регулярное выражение должно обрабатывать произвольные входные данные.When developing a pattern, you should consider how backtracking might affect the performance of the regular expression engine, particularly if your regular expression is designed to process unconstrained input. Дополнительные сведения см. в подразделе Грамотное использование поиска с возвратом.For more information, see the Take Charge of Backtracking section.

  • Следует тщательно протестировать регулярное выражение с использованием неподходящих и почти подходящих входных данных.Thoroughly test your regular expression using invalid and near-valid input as well as valid input. Для генерации произвольных входных данных для конкретного регулярного выражения можно использовать Rex — средство исследования регулярных выражений, разработанное группой Microsoft Research.To generate input for a particular regular expression randomly, you can use Rex, which is a regular expression exploration tool from Microsoft Research.

К началуBack to top

Правильное создание объектовHandle Object Instantiation Appropriately

Сердцем объектной модели регулярных выражений в .NET является класс System.Text.RegularExpressions.Regex, представляющий обработчик регулярных выражений.At the heart of .NET’s regular expression object model is the System.Text.RegularExpressions.Regex class, which represents the regular expression engine. Часто производительность регулярного выражения зависит именно от того, как используется обработчик Regex.Often, the single greatest factor that affects regular expression performance is the way in which the Regex engine is used. Определение регулярного выражения предполагает установление тесной взаимозависимости между обработчиком регулярных выражений и шаблоном регулярного выражения.Defining a regular expression involves tightly coupling the regular expression engine with a regular expression pattern. Процесс установления этой взаимозависимости является затратным, независимо от того, происходит ли создание объекта Regex путем передачи его конструктору шаблона регулярного выражения или происходит вызов статического метода путем передачи ему шаблона регулярного выражения и входной строки.That coupling process, whether it involves instantiating a Regex object by passing its constructor a regular expression pattern or calling a static method by passing it the regular expression pattern along with the string to be analyzed, is by necessity an expensive one.

Примечание

Более подробно производительно интерпретированные и скомпилированные регулярные выражения рассматриваются в записи блога Optimizing Regular Expression Performance, Part II: Taking Charge of Backtracking (Оптимизация производительности регулярных выражений, часть 2. Грамотное использование поиска с возвратом) в блоге команды BCL.For a more detailed discussion of the performance implications of using interpreted and compiled regular expressions, see Optimizing Regular Expression Performance, Part II: Taking Charge of Backtracking in the BCL Team blog.

Можно установить связь между обработчиком регулярных выражений и конкретным шаблоном регулярного выражения, а затем использовать обработчик для поиска совпадения в тексте несколькими способами.You can couple the regular expression engine with a particular regular expression pattern and then use the engine to match text in several ways:

  • Можно вызвать статический метод поиска совпадения с шаблоном, такой как Regex.Match(String, String).You can call a static pattern-matching method, such as Regex.Match(String, String). При этом не требуется создание объекта регулярного выражения.This does not require instantiation of a regular expression object.

  • Можно создать экземпляр объекта Regex и вызвать метод поиска совпадения с шаблоном этого экземпляра для интерпретированного регулярного выражения.You can instantiate a Regex object and call an instance pattern-matching method of an interpreted regular expression. Это метод по умолчанию для привязки обработчика регулярных выражений к шаблону регулярного выражения.This is the default method for binding the regular expression engine to a regular expression pattern. Он дает результат, когда объект Regex создается без аргумента options, включающего флажок Compiled.It results when a Regex object is instantiated without an options argument that includes the Compiled flag.

  • Можно создать экземпляр объекта Regex и вызвать метод поиска совпадения с шаблоном этого экземпляра для скомпилированного регулярного выражения.You can instantiate a Regex object and call an instance pattern-matching method of a compiled regular expression. Объекты регулярных выражений представляют скомпилированные шаблоны, когда объект Regex создается с аргументом options с флажком Compiled.Regular expression objects represent compiled patterns when a Regex object is instantiated with an options argument that includes the Compiled flag.

  • Можно целенаправленно создать объект Regex, связанный с конкретным шаблоном регулярного выражения, скомпилировать его и сохранить в отдельной сборке.You can create a special-purpose Regex object that is tightly coupled with a particular regular expression pattern, compile it, and save it to a standalone assembly. Это можно сделать, вызвав метод Regex.CompileToAssembly.You do this by calling the Regex.CompileToAssembly method.

Способ вызова методов сопоставления регулярных выражений может существенно влиять на ваше приложение.The particular way in which you call regular expression matching methods can have a significant impact on your application. В последующих разделах рассказывается, когда лучше использовать вызовы статического метода, интерпретированные регулярные выражения и скомпилированные регулярные выражения для повышения производительности приложения.The following sections discuss when to use static method calls, interpreted regular expressions, and compiled regular expressions to improve your application's performance.

Важно!

Способ вызова метода (статический, интерпретированный, скомпилированный) влияет на производительность, если одно регулярное выражение используется многократно при вызове методов или если приложение активно использует объекты регулярных выражений.The form of the method call (static, interpreted, compiled) affects performance if the same regular expression is used repeatedly in method calls, or if an application makes extensive use of regular expression objects.

Статические регулярные выраженияStatic Regular Expressions

Статические методы регулярных выражений рекомендуется использовать как альтернативу многократному созданию объектов регулярных выражений с одним регулярным выражением.Static regular expression methods are recommended as an alternative to repeatedly instantiating a regular expression object with the same regular expression. В отличие от шаблонов регулярных выражений, используемых объектами регулярных выражений, коды операций или скомпилированный MSIL-код шаблонов, используемых при вызове статического метода, кэшируются внутренне обработчиком регулярных выражений.Unlike regular expression patterns used by regular expression objects, either the operation codes or the compiled Microsoft intermediate language (MSIL) from patterns used in static method calls is cached internally by the regular expression engine.

Например, обработчик событий часто вызывает другой метод для проверки пользовательского ввода.For example, an event handler frequently calls another method to validate user input. Это отражено в следующем коде, где событие Button элемента управления Click используется для вызова метода с именем IsValidCurrency, проверяющего, что пользователь ввел обозначение денежной единицы и еще по крайней мере один десятичный знак.This is reflected in the following code, in which a Button control's Click event is used to call a method named IsValidCurrency, which checks whether the user has entered a currency symbol followed by at least one decimal digit.

public void OKButton_Click(object sender, EventArgs e) 
{
   if (! String.IsNullOrEmpty(sourceCurrency.Text))
      if (RegexLib.IsValidCurrency(sourceCurrency.Text))
         PerformConversion();
      else
         status.Text = "The source currency value is invalid.";
}
Public Sub OKButton_Click(sender As Object, e As EventArgs) _ 
           Handles OKButton.Click

   If Not String.IsNullOrEmpty(sourceCurrency.Text) Then
      If RegexLib.IsValidCurrency(sourceCurrency.Text) Then
         PerformConversion()
      Else
         status.Text = "The source currency value is invalid."
      End If          
   End If
End Sub

Очень неэффективная реализация метода IsValidCurrency показана в следующем примере.A very inefficient implementation of the IsValidCurrency method is shown in the following example. Обратите внимание, что при каждом вызове метода заново создается объект Regex с одним и тем же шаблоном.Note that each method call reinstantiates a Regex object with the same pattern. А это значит, что шаблон регулярного выражения перекомпилируется при каждом вызове метода.This, in turn, means that the regular expression pattern must be recompiled each time the method is called.

using System;
using System.Text.RegularExpressions;

public class RegexLib
{
   public static bool IsValidCurrency(string currencyValue)
   {
      string pattern = @"\p{Sc}+\s*\d+";
      Regex currencyRegex = new Regex(pattern);
      return currencyRegex.IsMatch(currencyValue);
   }
}
Imports System.Text.RegularExpressions

Public Module RegexLib
   Public Function IsValidCurrency(currencyValue As String) As Boolean
      Dim pattern As String = "\p{Sc}+\s*\d+"
      Dim currencyRegex As New Regex(pattern)
      Return currencyRegex.IsMatch(currencyValue) 
   End Function
End Module

Этот неэффективный код следует заменить вызовом статического метода Regex.IsMatch(String, String).You should replace this inefficient code with a call to the static Regex.IsMatch(String, String) method. Исчезнет необходимость заново создавать объект Regex при каждом вызове метода поиска совпадения с шаблоном, обработчик регулярных выражений сможет извлекать скомпилированную версию регулярного выражения из кэша.This eliminates the need to instantiate a Regex object each time you want to call a pattern-matching method, and enables the regular expression engine to retrieve a compiled version of the regular expression from its cache.

using System;
using System.Text.RegularExpressions;

public class RegexLib
{
   public static bool IsValidCurrency(string currencyValue)
   {
      string pattern = @"\p{Sc}+\s*\d+";
      return Regex.IsMatch(currencyValue, pattern); 
   }
}
Imports System.Text.RegularExpressions

Public Module RegexLib
   Public Function IsValidCurrency(currencyValue As String) As Boolean
      Dim pattern As String = "\p{Sc}+\s*\d+"
      Return Regex.IsMatch(currencyValue, pattern)
   End Function
End Module

По умолчанию кэшируется 15 последних использованных шаблонов статических регулярных выражений.By default, the last 15 most recently used static regular expression patterns are cached. Для приложений, которым требуется большее число кэшированных статических регулярных выражений, размер кэша можно задать с помощью свойства Regex.CacheSize.For applications that require a larger number of cached static regular expressions, the size of the cache can be adjusted by setting the Regex.CacheSize property.

Регулярное выражение \p{Sc}+\s*\d+, используемое в этом примере, проверяет, что входная строка состоит из обозначения валюты и по меньшей мере одного десятичного знака.The regular expression \p{Sc}+\s*\d+ that is used in this example verifies that the input string consists of a currency symbol and at least one decimal digit. Шаблон определяется, как показано в следующей таблице.The pattern is defined as shown in the following table.

ШаблонPattern ОПИСАНИЕDescription
\p{Sc}+ Совпадение с одним или несколькими символами Unicode Symbol, Currency (символ валюты в Юникоде).Match one or more characters in the Unicode Symbol, Currency category.
\s* Соответствует нулю или нескольким символам пробела.Match zero or more white-space characters.
\d+ Совпадение с одной или несколькими десятичными цифрами.Match one or more decimal digits.

Интерпретированные и Скомпилированные регулярные выраженияInterpreted vs. Compiled Regular Expressions

Шаблоны регулярных выражений, не привязанные к обработчику регулярных выражений указанием параметра Compiled, интерпретируются.Regular expression patterns that are not bound to the regular expression engine through the specification of the Compiled option are interpreted. При создании объекта регулярного выражения обработчик регулярных выражений преобразует регулярное выражение в набор кодов операций.When a regular expression object is instantiated, the regular expression engine converts the regular expression to a set of operation codes. При вызове метода экземпляра коды операций преобразуются в MSIL-код и выполняются JIT-компилятором.When an instance method is called, the operation codes are converted to MSIL and executed by the JIT compiler. Аналогично, при вызове статического метода регулярного выражения, если не удается найти регулярное выражение в кэше, обработчик регулярных выражений преобразует регулярное выражение в набор кодов операций и хранит их в кэше.Similarly, when a static regular expression method is called and the regular expression cannot be found in the cache, the regular expression engine converts the regular expression to a set of operation codes and stores them in the cache. Затем эти коды операций преобразуются в MSIL-код, чтобы JIT-компилятор мог выполнить их.It then converts these operation codes to MSIL so that the JIT compiler can execute them. Интерпретированные регулярные выражения снижают время запуска ценой более медленного выполнения.Interpreted regular expressions reduce startup time at the cost of slower execution time. Поэтому их лучше всего использовать, когда регулярное выражение используется с небольшим числом вызовов методов или если точное число вызовов методов регулярных выражений неизвестно, но предполагается, что оно будет небольшим.Because of this, they are best used when the regular expression is used in a small number of method calls, or if the exact number of calls to regular expression methods is unknown but is expected to be small. По мере увеличения числа вызовов методов выгоду по производительности от быстрого запуска перевешивает низкая скорость выполнения.As the number of method calls increases, the performance gain from reduced startup time is outstripped by the slower execution speed.

Шаблоны регулярных выражений, привязанные к обработчику регулярных выражений указанием параметра Compiled, компилируются.Regular expression patterns that are bound to the regular expression engine through the specification of the Compiled option are compiled. Это значит, что при создании объекта регулярного выражения или при вызове статического метода регулярного выражения, если не удается найти регулярное выражение в кэше, обработчик регулярных выражений преобразует регулярное выражение в промежуточный набор кодов операций, который затем преобразуется в MSIL-код.This means that, when a regular expression object is instantiated, or when a static regular expression method is called and the regular expression cannot be found in the cache, the regular expression engine converts the regular expression to an intermediary set of operation codes, which it then converts to MSIL. Когда вызывается метод, JIT-компилятор выполняет MSIL-код.When a method is called, the JIT compiler executes the MSIL. В отличие от интерпретированных регулярных выражений, скомпилированные регулярные выражения увеличивают время запуска, но позволяют выполнять отдельные методы поиска совпадения с шаблоном быстрее.In contrast to interpreted regular expressions, compiled regular expressions increase startup time but execute individual pattern-matching methods faster. В результате выгода по производительности от скомпилированных регулярных выражений увеличивается пропорционально числу вызовов методов регулярных выражений.As a result, the performance benefit that results from compiling the regular expression increases in proportion to the number of regular expression methods called.

Подводя итог, мы рекомендуем использовать интерпретированные регулярные выражения, когда методы регулярного выражения с конкретным регулярным выражением вызываются относительно редко.To summarize, we recommend that you use interpreted regular expressions when you call regular expression methods with a specific regular expression relatively infrequently. Скомпилированные регулярные выражения следует использовать, когда методы регулярного выражения с конкретным регулярным выражением вызываются относительно часто.You should use compiled regular expressions when you call regular expression methods with a specific regular expression relatively frequently. Точное пороговое значение, начиная с которого низкая скорость выполнения интерпретированных регулярных выражений перевешивает выгоду от быстрого запуска, или пороговое значение, при котором медленный запуск скомпилированных регулярных выражений перевешивает выгоду от быстрого выполнения, трудно указать.The exact threshold at which the slower execution speeds of interpreted regular expressions outweigh gains from their reduced startup time, or the threshold at which the slower startup times of compiled regular expressions outweigh gains from their faster execution speeds, is difficult to determine. Оно зависит от нескольких факторов, в том числе от сложности регулярного выражения и конкретных обрабатываемых данных.It depends on a variety of factors, including the complexity of the regular expression and the specific data that it processes. Чтобы определить, что позволит добиться лучшей производительности — интерпретированные или скомпилированные регулярные выражения, — можно использовать класс Stopwatch, чтобы сравнить время выполнения в обоих случаях.To determine whether interpreted or compiled regular expressions offer the best performance for your particular application scenario, you can use the Stopwatch class to compare their execution times.

В следующем примере сравнивается производительность скомпилированных и интерпретированных регулярных выражений при чтении первых десяти предложений, а также при чтении всех предложений текста книги Финансист Теодора Драйзера.The following example compares the performance of compiled and interpreted regular expressions when reading the first ten sentences and when reading all the sentences in the text of Theodore Dreiser's The Financier. Как показывает вывод примера, если требуется выполнить только 10 вызовов методов поиска совпадения, интерпретированное регулярное выражение дает более высокую производительность.As the output from the example shows, when only ten calls are made to regular expression matching methods, an interpreted regular expression offers better performance than a compiled regular expression. Однако скомпилированное регулярное выражение обеспечивает более высокую производительность при большом числе вызовов (в данном случае 13 000).However, a compiled regular expression offers better performance when a large number of calls (in this case, over 13,000) are made.

using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]";
      Stopwatch sw;
      Match match;
      int ctr;

      StreamReader inFile = new StreamReader(@".\Dreiser_TheFinancier.txt");
      string input = inFile.ReadToEnd();
      inFile.Close();
      
      // Read first ten sentences with interpreted regex.
      Console.WriteLine("10 Sentences with Interpreted Regex:");
      sw = Stopwatch.StartNew();
      Regex int10 = new Regex(pattern, RegexOptions.Singleline);
      match = int10.Match(input);
      for (ctr = 0; ctr <= 9; ctr++) {
         if (match.Success)
            // Do nothing with the match except get the next match.
            match = match.NextMatch();
         else
            break;
      }
      sw.Stop();
      Console.WriteLine("   {0} matches in {1}", ctr, sw.Elapsed);
      
      // Read first ten sentences with compiled regex.
      Console.WriteLine("10 Sentences with Compiled Regex:");
      sw = Stopwatch.StartNew();
      Regex comp10 = new Regex(pattern, 
                   RegexOptions.Singleline | RegexOptions.Compiled);
      match = comp10.Match(input);
      for (ctr = 0; ctr <= 9; ctr++) {
         if (match.Success)
            // Do nothing with the match except get the next match.
            match = match.NextMatch();
         else
            break;
      }
      sw.Stop();
      Console.WriteLine("   {0} matches in {1}", ctr, sw.Elapsed);
      
      // Read all sentences with interpreted regex.
      Console.WriteLine("All Sentences with Interpreted Regex:");
      sw = Stopwatch.StartNew();
      Regex intAll = new Regex(pattern, RegexOptions.Singleline);
      match = intAll.Match(input);
      int matches = 0;
      while (match.Success) {
         matches++;
         // Do nothing with the match except get the next match.
         match = match.NextMatch();
      }
      sw.Stop();
      Console.WriteLine("   {0:N0} matches in {1}", matches, sw.Elapsed);
      
      // Read all sentnces with compiled regex.
      Console.WriteLine("All Sentences with Compiled Regex:");
      sw = Stopwatch.StartNew();
      Regex compAll = new Regex(pattern, 
                      RegexOptions.Singleline | RegexOptions.Compiled);
      match = compAll.Match(input);
      matches = 0;
      while (match.Success) {
         matches++;
         // Do nothing with the match except get the next match.
         match = match.NextMatch();
      }
      sw.Stop();
      Console.WriteLine("   {0:N0} matches in {1}", matches, sw.Elapsed);      
   }
}
// The example displays the following output:
//       10 Sentences with Interpreted Regex:
//          10 matches in 00:00:00.0047491
//       10 Sentences with Compiled Regex:
//          10 matches in 00:00:00.0141872
//       All Sentences with Interpreted Regex:
//          13,443 matches in 00:00:01.1929928
//       All Sentences with Compiled Regex:
//          13,443 matches in 00:00:00.7635869
//       
//       >compare1
//       10 Sentences with Interpreted Regex:
//          10 matches in 00:00:00.0046914
//       10 Sentences with Compiled Regex:
//          10 matches in 00:00:00.0143727
//       All Sentences with Interpreted Regex:
//          13,443 matches in 00:00:01.1514100
//       All Sentences with Compiled Regex:
//          13,443 matches in 00:00:00.7432921
Imports System.Diagnostics
Imports System.IO
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As String = "\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]"
      Dim sw As Stopwatch
      Dim match As Match
      Dim ctr As Integer

      Dim inFile As New StreamReader(".\Dreiser_TheFinancier.txt")
      Dim input As String = inFile.ReadToEnd()
      inFile.Close()
      
      ' Read first ten sentences with interpreted regex.
      Console.WriteLine("10 Sentences with Interpreted Regex:")
      sw = Stopwatch.StartNew()
      Dim int10 As New Regex(pattern, RegexOptions.SingleLine)
      match = int10.Match(input)
      For ctr = 0 To 9
         If match.Success Then
            ' Do nothing with the match except get the next match.
            match = match.NextMatch()
         Else
            Exit For
         End If
      Next
      sw.Stop()
      Console.WriteLine("   {0} matches in {1}", ctr, sw.Elapsed)
      
      ' Read first ten sentences with compiled regex.
      Console.WriteLine("10 Sentences with Compiled Regex:")
      sw = Stopwatch.StartNew()
      Dim comp10 As New Regex(pattern, 
                   RegexOptions.SingleLine Or RegexOptions.Compiled)
      match = comp10.Match(input)
      For ctr = 0 To 9
         If match.Success Then
            ' Do nothing with the match except get the next match.
            match = match.NextMatch()
         Else
            Exit For
         End If
      Next
      sw.Stop()
      Console.WriteLine("   {0} matches in {1}", ctr, sw.Elapsed)
      
      ' Read all sentences with interpreted regex.
      Console.WriteLine("All Sentences with Interpreted Regex:")
      sw = Stopwatch.StartNew()
      Dim intAll As New Regex(pattern, RegexOptions.SingleLine)
      match = intAll.Match(input)
      Dim matches As Integer = 0
      Do While match.Success
         matches += 1
         ' Do nothing with the match except get the next match.
         match = match.NextMatch()
      Loop
      sw.Stop()
      Console.WriteLine("   {0:N0} matches in {1}", matches, sw.Elapsed)
      
      ' Read all sentnces with compiled regex.
      Console.WriteLine("All Sentences with Compiled Regex:")
      sw = Stopwatch.StartNew()
      Dim compAll As New Regex(pattern, 
                     RegexOptions.SingleLine Or RegexOptions.Compiled)
      match = compAll.Match(input)
      matches = 0
      Do While match.Success
         matches += 1
         ' Do nothing with the match except get the next match.
         match = match.NextMatch()
      Loop
      sw.Stop()
      Console.WriteLine("   {0:N0} matches in {1}", matches, sw.Elapsed)      
   End Sub
End Module
' The example displays output like the following:
'       10 Sentences with Interpreted Regex:
'          10 matches in 00:00:00.0047491
'       10 Sentences with Compiled Regex:
'          10 matches in 00:00:00.0141872
'       All Sentences with Interpreted Regex:
'          13,443 matches in 00:00:01.1929928
'       All Sentences with Compiled Regex:
'          13,443 matches in 00:00:00.7635869
'       
'       >compare1
'       10 Sentences with Interpreted Regex:
'          10 matches in 00:00:00.0046914
'       10 Sentences with Compiled Regex:
'          10 matches in 00:00:00.0143727
'       All Sentences with Interpreted Regex:
'          13,443 matches in 00:00:01.1514100
'       All Sentences with Compiled Regex:
'          13,443 matches in 00:00:00.7432921

Используемый в примере шаблон регулярного выражения \b(\w+((\r?\n)|,?\s))*\w+[.?:;!] определяется, как показано в следующей таблице.The regular expression pattern used in the example, \b(\w+((\r?\n)|,?\s))*\w+[.?:;!], is defined as shown in the following table.

ШаблонPattern ОПИСАНИЕDescription
\b Совпадение должно начинаться на границе слова.Begin the match at a word boundary.
\w+ Совпадение с одним или несколькими символами слова.Match one or more word characters.
(\r?\n)|,?\s) Соответствует нулю или одному возврату каретки и последующему символу новой строки; или нулю или одной запятой с последующим пробелом.Match either zero or one carriage return followed by a newline character, or zero or one comma followed by a white-space character.
(\w+((\r?\n)|,?\s))* Совпадение с одним или несколькими символами слова, за которыми следует ноль или один возврат каретки и символ новой строки, или ноль или одна запятая с последующим пробелом.Match zero or more occurrences of one or more word characters that are followed either by zero or one carriage return and a newline character, or by zero or one comma followed by a white-space character.
\w+ Совпадение с одним или несколькими символами слова.Match one or more word characters.
[.?:;!] Совпадение с точкой, вопросительным знаком, двоеточием, точкой с запятой или восклицательным знаком.Match a period, question mark, colon, semicolon, or exclamation point.

Регулярные выражения: скомпилированные в сборкуRegular Expressions: Compiled to an Assembly

Также .NET позволяет создать сборку, которая содержит скомпилированные регулярные выражения..NET also enables you to create an assembly that contains compiled regular expressions. Это позволяет переместить основные затраты на компиляцию регулярных выражений на время разработки (вместо времени выполнения).This moves the performance hit of regular expression compilation from run time to design time. Однако требуются дополнительные действия. Необходимо заранее определить регулярные выражения и скомпилировать их в сборку.However, it also involves some additional work: You must define the regular expressions in advance and compile them to an assembly. Затем компилятор может сослаться на эту сборку при компиляции исходного кода, использующего регулярные выражения из сборки.The compiler can then reference this assembly when compiling source code that uses the assembly’s regular expressions. Каждое скомпилированное регулярное выражение в сборке представлено классом, унаследованным от Regex.Each compiled regular expression in the assembly is represented by a class that derives from Regex.

Для компиляции регулярных выражений в сборку вызывается метод Regex.CompileToAssembly(RegexCompilationInfo[], AssemblyName) и ему передается массив объектов RegexCompilationInfo, представляющих регулярные выражения, которые требуется скомпилировать, и объект AssemblyName с информацией о создаваемой сборке.To compile regular expressions to an assembly, you call the Regex.CompileToAssembly(RegexCompilationInfo[], AssemblyName) method and pass it an array of RegexCompilationInfo objects that represent the regular expressions to be compiled, and an AssemblyName object that contains information about the assembly to be created.

Рекомендуется компилировать регулярные выражения в сборку в следующих случаях:We recommend that you compile regular expressions to an assembly in the following situations:

  • Если вы являетесь разработчиком компонентов и вам требуется создать библиотеку регулярных выражений для повторного использования.If you are a component developer who wants to create a library of reusable regular expressions.

  • Если вы предполагаете, что методы поиска совпадения с шаблоном вашего регулярного выражения будут вызываться неопределенное количество раз (от одного–двух раз до тысяч или десятков тысяч раз).If you expect your regular expression's pattern-matching methods to be called an indeterminate number of times -- anywhere from once or twice to thousands or tens of thousands of times. В отличие от интерпретированных и скомпилированных регулярных выражений, регулярные выражения, скомпилированные в отдельные сборки, обеспечивают стабильную производительность, независимо от числа вызовов методов.Unlike compiled or interpreted regular expressions, regular expressions that are compiled to separate assemblies offer performance that is consistent regardless of the number of method calls.

Если вы используете скомпилированные регулярные выражения для оптимизации производительности, не следует использовать отражение для создания сборки, загрузки обработчика регулярных выражений и выполнения его методов поиска совпадения с шаблоном.If you are using compiled regular expressions to optimize performance, you should not use reflection to create the assembly, load the regular expression engine, and execute its pattern-matching methods. Следует избегать динамического построения шаблонов регулярных выражений. Кроме того, необходимо задать все параметры поиска совпадения с шаблоном (например, чувствительность к регистру) в момент создания сборки.This requires that you avoid building regular expression patterns dynamically, and that you specify any pattern-matching options (such as case-insensitive pattern matching) at the time the assembly is created. Также необходимо отделить код, создающий сборку, от кода, использующего регулярное выражение.It also requires that you separate the code that creates the assembly from the code that uses the regular expression.

В следующем примере показано, как создать сборку, содержащую скомпилированное регулярное выражение.The following example shows how to create an assembly that contains a compiled regular expression. В примере создается сборка с именем RegexLib.dll с одним классом регулярных выражений, SentencePattern, содержащим шаблон регулярного выражения для поиска предложений, который использовался в разделе Интерпретированные и скомпилированные регулярные выражения.It creates an assembly named RegexLib.dll with a single regular expression class, SentencePattern, that contains the sentence-matching regular expression pattern used in the Interpreted vs. Compiled Regular Expressions section.

using System;
using System.Reflection;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      RegexCompilationInfo SentencePattern =
                           new RegexCompilationInfo(@"\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]",
                                                    RegexOptions.Multiline,
                                                    "SentencePattern",
                                                    "Utilities.RegularExpressions",
                                                    true);
      RegexCompilationInfo[] regexes = { SentencePattern };
      AssemblyName assemName = new AssemblyName("RegexLib, Version=1.0.0.1001, Culture=neutral, PublicKeyToken=null");
      Regex.CompileToAssembly(regexes, assemName);
   }
}
Imports System.Reflection
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim SentencePattern As New RegexCompilationInfo("\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]",
                                                      RegexOptions.Multiline,
                                                      "SentencePattern",
                                                      "Utilities.RegularExpressions",
                                                      True)
      Dim regexes() As RegexCompilationInfo = {SentencePattern}
      Dim assemName As New AssemblyName("RegexLib, Version=1.0.0.1001, Culture=neutral, PublicKeyToken=null")
      Regex.CompileToAssembly(regexes, assemName)
   End Sub
End Module

Когда код примера компилируется в исполняемый файл и выполняется, создается сборка с именем RegexLib.dll.When the example is compiled to an executable and run, it creates an assembly named RegexLib.dll. Регулярное выражение представлено классом с именем Utilities.RegularExpressions.SentencePattern, унаследованным от Regex.The regular expression is represented by a class named Utilities.RegularExpressions.SentencePattern that is derived from Regex. В следующем примере скомпилированное регулярное выражение используется для извлечения предложений из текста книги Финансист Теодора Драйзера.The following example then uses the compiled regular expression to extract the sentences from the text of Theodore Dreiser's The Financier.

using System;
using System.IO;
using System.Text.RegularExpressions;
using Utilities.RegularExpressions;

public class Example
{
   public static void Main()
   {
      SentencePattern pattern = new SentencePattern();
      StreamReader inFile = new StreamReader(@".\Dreiser_TheFinancier.txt");
      string input = inFile.ReadToEnd();
      inFile.Close();
      
      MatchCollection matches = pattern.Matches(input);
      Console.WriteLine("Found {0:N0} sentences.", matches.Count);      
   }
}
// The example displays the following output:
//      Found 13,443 sentences.
Imports System.IO
Imports System.Text.RegularExpressions
Imports Utilities.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As New SentencePattern()
      Dim inFile As New StreamReader(".\Dreiser_TheFinancier.txt")
      Dim input As String = inFile.ReadToEnd()
      inFile.Close()
      
      Dim matches As MatchCollection = pattern.Matches(input)
      Console.WriteLine("Found {0:N0} sentences.", matches.Count)      
   End Sub
End Module
' The example displays the following output:
'      Found 13,443 sentences.

К началуBack to top

Грамотное использование поиска с возвратомTake Charge of Backtracking

Обычно обработчик регулярных выражений двигается по входной строке линейным образом, сравнивая ее с шаблоном регулярного выражения.Ordinarily, the regular expression engine uses linear progression to move through an input string and compare it to a regular expression pattern. Однако когда в шаблоне регулярного выражения используются неопределенные квантификаторы, такие как *, + или ?, обработчик регулярных выражений может отбрасывать частичное совпадение и возвращаться к ранее сохраненному состоянию, чтобы искать совпадение с шаблоном целиком.However, when indeterminate quantifiers such as *, +, and ? are used in a regular expression pattern, the regular expression engine may give up a portion of successful partial matches and return to a previously saved state in order to search for a successful match for the entire pattern. Этот процесс известен как поиск с возвратом.This process is known as backtracking.

Примечание

Дополнительные сведения о поиске с возвратом см. в статьях Подробные сведения о поведении регулярных выражений и Поиск с возвратом в регулярных выражениях.For more information on backtracking, see Details of Regular Expression Behavior and Backtracking. Поиска с возвратом подробно рассматривается в записи блога Optimizing Regular Expression Performance, Part II: Taking Charge of Backtracking (Оптимизация производительности регулярных выражений, часть 2. Грамотное использование поиска с возвратом) в блоге команды BCL.For a detailed discussion of backtracking, see Optimizing Regular Expression Performance, Part II: Taking Charge of Backtracking in the BCL Team blog.

Поддержка поиска с возвратом обеспечивает мощность и гибкость регулярных выражений.Support for backtracking gives regular expressions power and flexibility. При этом ответственность за управление работой обработчика регулярных выражений лежит на разработчике регулярных выражений.It also places the responsibility for controlling the operation of the regular expression engine in the hands of regular expression developers. Поскольку разработчики часто не отдают себе отчет в этой ответственности, неправильное или излишнее использование поиска с возвратом часто становится причиной снижения производительности регулярных выражений.Because developers are often not aware of this responsibility, their misuse of backtracking or reliance on excessive backtracking often plays the most significant role in degrading regular expression performance. В самом неблагоприятном случае время обработки может удваиваться при каждом добавлении символа во входную строку.In a worst-case scenario, execution time can double for each additional character in the input string. Чрезмерно используя поиск с возвратом, очень легко создать программный аналог бесконечной петли, если входные данные почти совпадают с шаблоном регулярного выражения. Обработчик регулярных выражений может в течение часов или даже дней обрабатывать относительно короткую входную строку.In fact, by using backtracking excessively, it is easy to create the programmatic equivalent of an endless loop if input nearly matches the regular expression pattern; the regular expression engine may take hours or even days to process a relatively short input string.

Часто приложения имеют сниженную производительность из-за использования поиска с возвратом, хотя поиск с возвратом не очень важен для обнаружения совпадения.Often, applications pay a performance penalty for using backtracking despite the fact that backtracking is not essential for a match. Например, регулярное выражение \b\p{Lu}\w*\b совпадает со всеми словами, начинающимися с символа верхнего регистра, как показано в следующей таблице.For example, the regular expression \b\p{Lu}\w*\b matches all words that begin with an uppercase character, as the following table shows.

ШаблонPattern ОПИСАНИЕDescription
\b Совпадение должно начинаться на границе слова.Begin the match at a word boundary.
\p{Lu} Совпадение с символом верхнего регистра.Match an uppercase character.
\w* Совпадение с нулем или большим числом буквенных символов.Match zero or more word characters.
\b Совпадение должно заканчиваться на границе слова.End the match at a word boundary.

Поскольку граница слова не является символом слова (или подмножеством символов слова), нет шанса, что регулярное выражение перейдет границу слова, сопоставляя символы слова.Because a word boundary is not the same as, or a subset of, a word character, there is no possibility that the regular expression engine will cross a word boundary when matching word characters. Это значит, что для этого регулярного выражения поиск с возвратом не принесет никакой пользы, а только снизит производительность, поскольку обработчик регулярных выражений вынужден сохранять свое состояние для каждого возможного совпадения.This means that for this regular expression, backtracking can never contribute to the overall success of any match -- it can only degrade performance, because the regular expression engine is forced to save its state for each successful preliminary match of a word character.

Если вы понимаете, что нет необходимости использовать поиск с возвратом, его можно отключить с помощью языкового элемента (?>subexpression).If you determine that backtracking is not necessary, you can disable it by using the (?>subexpression) language element. В следующем примере производится анализ входной строки с использованием двух регулярных выражений.The following example parses an input string by using two regular expressions. Функционирование первого регулярного выражения, \b\p{Lu}\w*\b, основано на поиске с возвратом.The first, \b\p{Lu}\w*\b, relies on backtracking. Второе регулярное выражение, \b\p{Lu}(?>\w*)\b, отключает поиск с возвратом.The second, \b\p{Lu}(?>\w*)\b, disables backtracking. Как видно по выводу, выражения дают одинаковый результат.As the output from the example shows, they both produce the same result.

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string input = "This this word Sentence name Capital";
      string pattern = @"\b\p{Lu}\w*\b";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine(match.Value);

      Console.WriteLine();
      
      pattern = @"\b\p{Lu}(?>\w*)\b";   
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine(match.Value);
   }
}
// The example displays the following output:
//       This
//       Sentence
//       Capital
//       
//       This
//       Sentence
//       Capital
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim input As String = "This this word Sentence name Capital"
      Dim pattern As String = "\b\p{Lu}\w*\b"
      For Each match As Match In Regex.Matches(input, pattern)
         Console.WriteLine(match.Value)
      Next
      Console.WriteLine()
      
      pattern = "\b\p{Lu}(?>\w*)\b"   
      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
'       Sentence
'       Capital
'       
'       This
'       Sentence
'       Capital

Часто поиск с возвратом очень важен для поиска во входной строке совпадения с шаблоном регулярного выражения.In many cases, backtracking is essential for matching a regular expression pattern to input text. Тем не менее избыточное использование поиска с возвратом может сильно снизить производительность и создать впечатление, что приложение перестало отвечать.However, excessive backtracking can severely degrade performance and create the impression that an application has stopped responding. Например, так происходит, когда используются вложенные квантификаторы и текст, совпадающий со внешней частью выражения, является подмножеством текста, совпадающего со внутренней частью выражения.In particular, this happens when quantifiers are nested and the text that matches the outer subexpression is a subset of the text that matches the inner subexpression.

Предупреждение

Помимо того, что следует избегать избыточного использования поиска с возвратом, необходимо использовать возможность времени ожидания, чтобы убедиться, что избыточный поиск с возвратом не слишком сильно снижает производительность регулярного выражения.In addition to avoiding excessive backtracking, you should use the timeout feature to ensure that excessive backtracking does not severely degrade regular expression performance. Дополнительные сведения см. в статье Использование значений времени ожидания.For more information, see the Use Time-out Values section.

Например, шаблон регулярного выражения ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$ должен искать номер части, состоящий из по крайней мере одного алфавитно-цифрового символа.For example, the regular expression pattern ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$ is intended to match a part number that consists of at least one alphanumeric character. Дополнительные символы могут включать в себя алфавитно-цифровые символы, дефис, подчеркивание или точку, но последний символ должен быть алфавитно-цифровым.Any additional characters can consist of an alphanumeric character, a hyphen, an underscore, or a period, though the last character must be alphanumeric. Знак доллара завершает номер части.A dollar sign terminates the part number. В некоторых случаях этот шаблон регулярного выражения может привести к очень низкой производительности, поскольку в нем используются вложенные квантификаторы и часть выражения [0-9A-Z] является подмножеством части выражения [-.\w]*.In some cases, this regular expression pattern can exhibit extremely poor performance because quantifiers are nested, and because the subexpression [0-9A-Z] is a subset of the subexpression [-.\w]*.

В таких случаях можно оптимизировать производительность, удалив вложенные квантификаторы и заменив внешнюю часть выражения утверждением просмотра вперед или назад нулевой ширины.In these cases, you can optimize regular expression performance by removing the nested quantifiers and replacing the outer subexpression with a zero-width lookahead or lookbehind assertion. Утверждения просмотра вперед и назад являются "якорями". Они не перемещают указатель во входной строке, а выполняют поиск впереди или позади, проверяя, выполняется ли заданное условие.Lookahead and lookbehind assertions are anchors; they do not move the pointer in the input string, but instead look ahead or behind to check whether a specified condition is met. Например, регулярное выражение для поиска номера части можно записать следующим образом: ^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$.For example, the part number regular expression can be rewritten as ^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$. Этот шаблон регулярного выражения определяется, как показано в следующей таблице.This regular expression pattern is defined as shown in the following table.

ШаблонPattern ОПИСАНИЕDescription
^ Начало совпадения в начале входной строки.Begin the match at the beginning of the input string.
[0-9A-Z] Совпадение с алфавитно-цифровым символом.Match an alphanumeric character. Номер части должен состоять по меньшей мере из этого символа.The part number must consist of at least this character.
[-.\w]* Совпадение с нулем или большим числом вхождений любого символа слова, дефиса или точки.Match zero or more occurrences of any word character, hyphen, or period.
\$ Совпадение со знаком доллара.Match a dollar sign.
(?<=[0-9A-Z]) Поиск за конечным знаком доллара с целью проверить, что предыдущий символ алфавитно-цифровой.Look ahead of the ending dollar sign to ensure that the previous character is alphanumeric.
$ Совпадение должно заканчиваться в конце входной строки.End the match at the end of the input string.

В приведенном ниже примере демонстрируется использование этого регулярного выражения для поиска совпадений в массиве потенциальных номеров частей.The following example illustrates the use of this regular expression to match an array containing possible part numbers.

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$";
      string[] partNos = { "A1C$", "A4", "A4$", "A1603D$", "A1603D#" };
      
      foreach (var input in partNos) {
         Match match = Regex.Match(input, pattern);
         if (match.Success)
            Console.WriteLine(match.Value);
         else
            Console.WriteLine("Match not found.");
      }      
   }
}
// The example displays the following output:
//       A1C$
//       Match not found.
//       A4$
//       A1603D$
//       Match not found.
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As String = "^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$"
      Dim partNos() As String = { "A1C$", "A4", "A4$", "A1603D$", 
                                  "A1603D#" }
      
      For Each input As String In partNos
         Dim match As Match = Regex.Match(input, pattern)
         If match.Success Then
            Console.WriteLine(match.Value)
         Else
            Console.WriteLine("Match not found.")
         End If
      Next      
   End Sub
End Module
' The example displays the following output:
'       A1C$
'       Match not found.
'       A4$
'       A1603D$
'       Match not found.

Язык регулярных выражений в .NET включает следующие языковые элементы, которые можно использовать для исключения вложенных квантификаторов.The regular expression language in .NET includes the following language elements that you can use to eliminate nested quantifiers. Для получения дополнительной информации см. Конструкции группирования.For more information, see Grouping Constructs.

Элемент языкаLanguage element ОПИСАНИЕDescription
(?= subexpression )(?= subexpression ) Положительный просмотр вперед нулевой ширины.Zero-width positive lookahead. Поиск за текущей позицией с целью проверить, совпадает ли параметр subexpression со входной строкой.Look ahead of the current position to determine whether subexpression matches the input string.
(?! subexpression )(?! subexpression ) Отрицательный просмотр вперед нулевой ширины.Zero-width negative lookahead. Поиск за текущей позицией с целью проверить, что параметр subexpression не совпадает со входной строкой.Look ahead of the current position to determine whether subexpression does not match the input string.
(?<= subexpression )(?<= subexpression ) Положительный просмотр назад нулевой ширины.Zero-width positive lookbehind. Поиск перед текущей позицией с целью проверить, совпадает ли параметр subexpression со входной строкой.Look behind the current position to determine whether subexpression matches the input string.
(?<! subexpression )(?<! subexpression ) Отрицательный просмотр назад нулевой ширины.Zero-width negative lookbehind. Поиск перед текущей позицией с целью проверить, что параметр subexpression не совпадает со входной строкой.Look behind the current position to determine whether subexpression does not match the input string.

К началуBack to top

Использование значений времени ожиданияUse Time-out Values

Если регулярные выражения обрабатывают входные данные, которые почти совпадают с шаблоном регулярного выражения, зачастую они могут использовать избыточный поиск с возвратом, что сильно влияет на их производительность.If your regular expressions processes input that nearly matches the regular expression pattern, it can often rely on excessive backtracking, which impacts its performance significantly. Помимо того, что следует тщательно обдумывать использование поиска с возвратом и тестировать регулярное выражение на почти совпадающих входных данных, необходимо всегда устанавливать значение времени ожидания для минимизации влияния на производительность избыточного поиска с возвратом, если он все же используется.In addition to carefully considering your use of backtracking and testing the regular expression against near-matching input, you should always set a time-out value to ensure that the impact of excessive backtracking, if it occurs, is minimized.

Интервал времени ожидания регулярного выражения определяет период времени, в течение которого обработчик регулярных выражений выполняет поиск одного совпадения, после чего время ожидания истекает. По умолчанию интервал времени ожидания равен Regex.InfiniteMatchTimeout, что означает, что регулярное выражение имеет неограниченное время ожидания. Можно переопределить это значение и задать интервал времени ожидания следующим образом:The regular expression time-out interval defines the period of time that the regular expression engine will look for a single match before it times out. The default time-out interval is Regex.InfiniteMatchTimeout, which means that the regular expression will not time out. You can override this value and define a time-out interval as follows:

Если определен интервал времени ожидания и по истечении этого интервала совпадение не найдено, метод регулярного выражения вызывает исключение RegexMatchTimeoutException.If you have defined a time-out interval and a match is not found at the end of that interval, the regular expression method throws a RegexMatchTimeoutException exception. В обработчике исключений можно принять решение о том, повторить ли поиск совпадения с более длинным интервалом времени ожидания, отказаться от попытки поиска совпадения и предположить, что совпадение не найдено, или же отказаться от попытки поиска совпадения и зарегистрировать сведения об исключении в журнале для анализа в дальнейшем.In your exception handler, you can choose to retry the match with a longer time-out interval, abandon the match attempt and assume that there is no match, or abandon the match attempt and log the exception information for future analysis.

В следующем примере определяется метод GetWordData, который создает регулярное выражение с интервалом времени ожидания 350 миллисекунд, чтобы вычислить количество слов и среднее количество символов в слове в текстовом документе.The following example defines a GetWordData method that instantiates a regular expression with a time-out interval of 350 milliseconds to calculate the number of words and average number of characters in a word in a text document. Если время ожидания для операции поиска совпадения истекает, интервал времени ожидания увеличивается на 350 миллисекунд и объект Regex создается заново.If the matching operation times out, the time-out interval is increased by 350 milliseconds and the Regex object is re-instantiated. Если новый интервал времени ожидания превышает 1 секунду, метод передает исключение вызывающему объекту.If the new time-out interval exceeds 1 second, the method re-throws the exception to the caller.

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

public class Example
{
   public static void Main()
   {
      RegexUtilities util = new RegexUtilities();
      string title = "Doyle - The Hound of the Baskervilles.txt";
      try {
         var info = util.GetWordData(title);
         Console.WriteLine("Words:               {0:N0}", info.Item1);
         Console.WriteLine("Average Word Length: {0:N2} characters", info.Item2); 
      }
      catch (IOException e) {
         Console.WriteLine("IOException reading file '{0}'", title);
         Console.WriteLine(e.Message);
      }
      catch (RegexMatchTimeoutException e) {
         Console.WriteLine("The operation timed out after {0:N0} milliseconds", 
                           e.MatchTimeout.TotalMilliseconds);
      }
   }
}

public class RegexUtilities
{
   public Tuple<int, double> GetWordData(string filename)
   { 
      const int MAX_TIMEOUT = 1000;   // Maximum timeout interval in milliseconds.
      const int INCREMENT = 350;      // Milliseconds increment of timeout.
      
      List<string> exclusions = new List<string>( new string[] { "a", "an", "the" });
      int[] wordLengths = new int[29];        // Allocate an array of more than ample size.
      string input = null;
      StreamReader sr = null;
      try { 
         sr = new StreamReader(filename);
         input = sr.ReadToEnd();
      }
      catch (FileNotFoundException e) {
         string msg = String.Format("Unable to find the file '{0}'", filename);
         throw new IOException(msg, e);
      }
      catch (IOException e) {
         throw new IOException(e.Message, e);
      }
      finally {
         if (sr != null) sr.Close(); 
      }

      int timeoutInterval = INCREMENT;
      bool init = false;
      Regex rgx = null;
      Match m = null;
      int indexPos = 0;  
      do {
         try {
            if (! init) {
               rgx = new Regex(@"\b\w+\b", RegexOptions.None, 
                               TimeSpan.FromMilliseconds(timeoutInterval));
               m = rgx.Match(input, indexPos);
               init = true;
            }
            else { 
               m = m.NextMatch();
            }
            if (m.Success) {    
               if ( !exclusions.Contains(m.Value.ToLower()))
                  wordLengths[m.Value.Length]++;

               indexPos += m.Length + 1;   
            }
         }
         catch (RegexMatchTimeoutException e) {
            if (e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT) {
               timeoutInterval += INCREMENT;
               init = false;
            }
            else {
               // Rethrow the exception.
               throw; 
            }   
         }          
      } while (m.Success);
            
      // If regex completed successfully, calculate number of words and average length.
      int nWords = 0; 
      long totalLength = 0;
      
      for (int ctr = wordLengths.GetLowerBound(0); ctr <= wordLengths.GetUpperBound(0); ctr++) {
         nWords += wordLengths[ctr];
         totalLength += ctr * wordLengths[ctr];
      }
      return new Tuple<int, double>(nWords, totalLength/nWords);
   }
}
Imports System.Collections.Generic
Imports System.IO
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim util As New RegexUtilities()
      Dim title As String = "Doyle - The Hound of the Baskervilles.txt"
      Try
         Dim info = util.GetWordData(title)
         Console.WriteLine("Words:               {0:N0}", info.Item1)
         Console.WriteLine("Average Word Length: {0:N2} characters", info.Item2) 
      Catch e As IOException
         Console.WriteLine("IOException reading file '{0}'", title)
         Console.WriteLine(e.Message)
      Catch e As RegexMatchTimeoutException
         Console.WriteLine("The operation timed out after {0:N0} milliseconds", 
                           e.MatchTimeout.TotalMilliseconds)
      End Try
   End Sub
End Module

Public Class RegexUtilities
   Public Function GetWordData(filename As String) As Tuple(Of Integer, Double) 
      Const MAX_TIMEOUT As Integer = 1000  ' Maximum timeout interval in milliseconds.
      Const INCREMENT As Integer = 350     ' Milliseconds increment of timeout.
      
      Dim exclusions As New List(Of String)({"a", "an", "the" })
      Dim wordLengths(30) As Integer        ' Allocate an array of more than ample size.
      Dim input As String = Nothing
      Dim sr As StreamReader = Nothing
      Try 
         sr = New StreamReader(filename)
         input = sr.ReadToEnd()
      Catch e As FileNotFoundException
         Dim msg As String = String.Format("Unable to find the file '{0}'", filename)
         Throw New IOException(msg, e)
      Catch e As IOException
         Throw New IOException(e.Message, e)
      Finally
         If sr IsNot Nothing Then sr.Close() 
      End Try

      Dim timeoutInterval As Integer = INCREMENT
      Dim init As Boolean = False
      Dim rgx As Regex = Nothing
      Dim m As Match = Nothing
      Dim indexPos As Integer = 0  
      Do
         Try
            If Not init Then
               rgx = New Regex("\b\w+\b", RegexOptions.None, 
                               TimeSpan.FromMilliseconds(timeoutInterval))
               m = rgx.Match(input, indexPos)
               init = True
            Else 
               m = m.NextMatch()
            End If
            If m.Success Then    
               If Not exclusions.Contains(m.Value.ToLower()) Then
                  wordLengths(m.Value.Length) += 1
               End If
               indexPos += m.Length + 1   
            End If
         Catch e As RegexMatchTimeoutException
            If e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT Then
               timeoutInterval += INCREMENT
               init = False
            Else
               ' Rethrow the exception.
               Throw 
            End If   
         End Try          
      Loop While m.Success
            
      ' If regex completed successfully, calculate number of words and average length.
      Dim nWords As Integer
      Dim totalLength As Long
      
      For ctr As Integer = wordLengths.GetLowerBound(0) To wordLengths.GetUpperBound(0)
         nWords += wordLengths(ctr)
         totalLength += ctr * wordLengths(ctr)
      Next
      Return New Tuple(Of Integer, Double)(nWords, totalLength/nWords)
   End Function
End Class

К началуBack to top

Захват только в случае необходимостиCapture Only When Necessary

Регулярные выражения в .NET поддерживают ряд конструкций группирования, позволяющих группировать шаблон регулярного выражения в виде одной или более частей.Regular expressions in .NET support a number of grouping constructs, which let you group a regular expression pattern into one or more subexpressions. Наиболее часто в языке регулярных выражений .NET используются конструкции группирования (часть_выражения), которая определяет нумерованную группу записи, и (?<имя>часть_выражения), которая определяет именованную группу записи.The most commonly used grouping constructs in .NET regular expression language are (subexpression), which defines a numbered capturing group, and (?<name>subexpression), which defines a named capturing group. Конструкции группирования крайне важны для создания обратных ссылок и для определения части выражения, к которой должен применяться квантификатор.Grouping constructs are essential for creating backreferences and for defining a subexpression to which a quantifier is applied.

Однако использование этих языковых элементов имеет последствия.However, the use of these language elements has a cost. Объект GroupCollection, возвращаемый свойством Match.Groups заполняется наиболее новыми неименованными или именованными захваченными объектами, и если одна конструкция группирования захватила несколько подстрок из входной строки, они заполняют объект CaptureCollection, возвращаемый свойством Group.Captures определенной группы захвата с несколькими объектами Capture.They cause the GroupCollection object returned by the Match.Groups property to be populated with the most recent unnamed or named captures, and if a single grouping construct has captured multiple substrings in the input string, they also populate the CaptureCollection object returned by the Group.Captures property of a particular capturing group with multiple Capture objects.

Часто конструкции группирования используются в регулярном выражении только для того, чтобы к нему можно было применить квантификаторы, и группы, захваченные этими частями выражения затем не используются.Often, grouping constructs are used in a regular expression only so that quantifiers can be applied to them, and the groups captured by these subexpressions are not subsequently used. Например, регулярное выражение \b(\w+[;,]?\s?)+[.?!] предназначено для записи всего предложения.For example, the regular expression \b(\w+[;,]?\s?)+[.?!] is designed to capture an entire sentence. В следующей таблице описаны языковые элементы этого шаблона регулярных выражений и их влияние на коллекции Match и Match.Groups объекта Group.Captures.The following table describes the language elements in this regular expression pattern and their effect on the Match object's Match.Groups and Group.Captures collections.

ШаблонPattern ОПИСАНИЕDescription
\b Совпадение должно начинаться на границе слова.Begin the match at a word boundary.
\w+ Совпадение с одним или несколькими символами слова.Match one or more word characters.
[;,]? Совпадение с нулем или одной запятой или точкой с запятой.Match zero or one comma or semicolon.
\s? Совпадение с нулем или одним символом пробела.Match zero or one white-space character.
(\w+[;,]?\s?)+ Совпадение с одним или большим числом символов слова; затем, необязательно, запятой или точкой с запятой; затем, необязательно, пробелом.Match one or more occurrences of one or more word characters followed by an optional comma or semicolon followed by an optional white-space character. Здесь определена первая группа захвата, которая необходима, чтобы набор нескольких символов слова (то есть слово) и необязательно следующий за ним знак препинания повторялись, пока регулярное выражение не достигнет конца предложения.This defines the first capturing group, which is necessary so that the combination of multiple word characters (that is, a word) followed by an optional punctuation symbol will be repeated until the regular expression engine reaches the end of a sentence.
[.?!] Совпадение с точкой, вопросительным знаком или восклицательным знаком.Match a period, question mark, or exclamation point.

Как показано в следующем примере, когда совпадение найдено, объекты GroupCollection и CaptureCollection заполняются захваченными объектами из совпадения.As the following example shows, when a match is found, both the GroupCollection and CaptureCollection objects are populated with captures from the match. В этом случае используется группа записи (\w+[;,]?\s?), чтобы к ней можно было применить квантификатор +, который позволяет шаблону регулярного выражения сопоставить каждое слово в предложении.In this case, the capturing group (\w+[;,]?\s?) exists so that the + quantifier can be applied to it, which enables the regular expression pattern to match each word in a sentence. В противном случае было бы найдено совпадение только с последним словом предложения.Otherwise, it would match the last word in a sentence.

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string input = "This is one sentence. This is another.";
      string pattern = @"\b(\w+[;,]?\s?)+[.?!]";
      
      foreach (Match match in Regex.Matches(input, pattern)) {
         Console.WriteLine("Match: '{0}' at index {1}.", 
                           match.Value, match.Index);
         int grpCtr = 0;
         foreach (Group grp in match.Groups) {
            Console.WriteLine("   Group {0}: '{1}' at index {2}.",
                              grpCtr, grp.Value, grp.Index);
            int capCtr = 0;
            foreach (Capture cap in grp.Captures) {
               Console.WriteLine("      Capture {0}: '{1}' at {2}.",
                                 capCtr, cap.Value, cap.Index);
               capCtr++;
            }
            grpCtr++;
         }          
         Console.WriteLine();        
      }
   }
}
// The example displays the following output:
//       Match: 'This is one sentence.' at index 0.
//          Group 0: 'This is one sentence.' at index 0.
//             Capture 0: 'This is one sentence.' at 0.
//          Group 1: 'sentence' at index 12.
//             Capture 0: 'This ' at 0.
//             Capture 1: 'is ' at 5.
//             Capture 2: 'one ' at 8.
//             Capture 3: 'sentence' at 12.
//       
//       Match: 'This is another.' at index 22.
//          Group 0: 'This is another.' at index 22.
//             Capture 0: 'This is another.' at 22.
//          Group 1: 'another' at index 30.
//             Capture 0: 'This ' at 22.
//             Capture 1: 'is ' at 27.
//             Capture 2: 'another' at 30.
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim input As String = "This is one sentence. This is another."
      Dim pattern As String = "\b(\w+[;,]?\s?)+[.?!]"
      
      For Each match As Match In Regex.Matches(input, pattern)
         Console.WriteLine("Match: '{0}' at index {1}.", 
                           match.Value, match.Index)
         Dim grpCtr As Integer = 0
         For Each grp As Group In match.Groups
            Console.WriteLine("   Group {0}: '{1}' at index {2}.",
                              grpCtr, grp.Value, grp.Index)
            Dim capCtr As Integer = 0
            For Each cap As Capture In grp.Captures
               Console.WriteLine("      Capture {0}: '{1}' at {2}.",
                                 capCtr, cap.Value, cap.Index)
               capCtr += 1
            Next
            grpCtr += 1
         Next          
         Console.WriteLine()        
      Next    
   End Sub
End Module
' The example displays the following output:
'       Match: 'This is one sentence.' at index 0.
'          Group 0: 'This is one sentence.' at index 0.
'             Capture 0: 'This is one sentence.' at 0.
'          Group 1: 'sentence' at index 12.
'             Capture 0: 'This ' at 0.
'             Capture 1: 'is ' at 5.
'             Capture 2: 'one ' at 8.
'             Capture 3: 'sentence' at 12.
'       
'       Match: 'This is another.' at index 22.
'          Group 0: 'This is another.' at index 22.
'             Capture 0: 'This is another.' at 22.
'          Group 1: 'another' at index 30.
'             Capture 0: 'This ' at 22.
'             Capture 1: 'is ' at 27.
'             Capture 2: 'another' at 30.

Если части выражения используются только для применения к ним квантификаторов и захваченный текст не нужен, следует отменить захваты групп.When you use subexpressions only to apply quantifiers to them, and you are not interested in the captured text, you should disable group captures. Например, языковой элемент (?:subexpression) запрещает группе, к которой он применен, захватывать совпавшие подстроки.For example, the (?:subexpression) language element prevents the group to which it applies from capturing matched substrings. В следующем примере шаблон регулярного выражения из предыдущего примера изменен на \b(?:\w+[;,]?\s?)+[.?!].In the following example, the regular expression pattern from the previous example is changed to \b(?:\w+[;,]?\s?)+[.?!]. Как показывает вывод, обработчик регулярных выражений не заполняет коллекции GroupCollection и CaptureCollection.As the output shows, it prevents the regular expression engine from populating the GroupCollection and CaptureCollection collections.

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string input = "This is one sentence. This is another.";
      string pattern = @"\b(?:\w+[;,]?\s?)+[.?!]";
      
      foreach (Match match in Regex.Matches(input, pattern)) {
         Console.WriteLine("Match: '{0}' at index {1}.", 
                           match.Value, match.Index);
         int grpCtr = 0;
         foreach (Group grp in match.Groups) {
            Console.WriteLine("   Group {0}: '{1}' at index {2}.",
                              grpCtr, grp.Value, grp.Index);
            int capCtr = 0;
            foreach (Capture cap in grp.Captures) {
               Console.WriteLine("      Capture {0}: '{1}' at {2}.",
                                 capCtr, cap.Value, cap.Index);
               capCtr++;
            }
            grpCtr++;
         }          
         Console.WriteLine();        
      }
   }
}
// The example displays the following output:
//       Match: 'This is one sentence.' at index 0.
//          Group 0: 'This is one sentence.' at index 0.
//             Capture 0: 'This is one sentence.' at 0.
//       
//       Match: 'This is another.' at index 22.
//          Group 0: 'This is another.' at index 22.
//             Capture 0: 'This is another.' at 22.
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim input As String = "This is one sentence. This is another."
      Dim pattern As String = "\b(?:\w+[;,]?\s?)+[.?!]"
      
      For Each match As Match In Regex.Matches(input, pattern)
         Console.WriteLine("Match: '{0}' at index {1}.", 
                           match.Value, match.Index)
         Dim grpCtr As Integer = 0
         For Each grp As Group In match.Groups
            Console.WriteLine("   Group {0}: '{1}' at index {2}.",
                              grpCtr, grp.Value, grp.Index)
            Dim capCtr As Integer = 0
            For Each cap As Capture In grp.Captures
               Console.WriteLine("      Capture {0}: '{1}' at {2}.",
                                 capCtr, cap.Value, cap.Index)
               capCtr += 1
            Next
            grpCtr += 1
         Next          
         Console.WriteLine()        
      Next    
   End Sub
End Module
' The example displays the following output:
'       Match: 'This is one sentence.' at index 0.
'          Group 0: 'This is one sentence.' at index 0.
'             Capture 0: 'This is one sentence.' at 0.
'       
'       Match: 'This is another.' at index 22.
'          Group 0: 'This is another.' at index 22.
'             Capture 0: 'This is another.' at 22.

Отключить захват можно одним из следующих способов:You can disable captures in one of the following ways:

  • Используйте языковой элемент (?:subexpression).Use the (?:subexpression) language element. Этот элемент отключает захват совпавших подстрок в группе, к которой он применен.This element prevents the capture of matched substrings in the group to which it applies. Во вложенных группах захват подстрок не отключается.It does not disable substring captures in any nested groups.

  • Использовать параметр ExplicitCapture.Use the ExplicitCapture option. Он отключает все неименованные и неявные захваты для шаблона регулярных выражений.It disables all unnamed or implicit captures in the regular expression pattern. При использовании этого параметра захват выполняется только для тех подстрок, которые совпадают с именованными группами, определенными с помощью языкового элемента (?<name>subexpression).When you use this option, only substrings that match named groups defined with the (?<name>subexpression) language element can be captured. Флажок ExplicitCapture можно передать в параметр options конструктора класса Regex или в параметр options статического метода поиска совпадения Regex.The ExplicitCapture flag can be passed to the options parameter of a Regex class constructor or to the options parameter of a Regex static matching method.

  • Использовать параметр n в языковом элементе (?imnsx).Use the n option in the (?imnsx) language element. Этот параметр отключает все неименованные или неявные захваты, начиная с того места, на котором находится этот элемент в шаблоне регулярного выражения.This option disables all unnamed or implicit captures from the point in the regular expression pattern at which the element appears. Захват отключается либо до конца шаблона, либо пока захват для неименованных и неявных объектов не будет включен параметром (-n).Captures are disabled either until the end of the pattern or until the (-n) option enables unnamed or implicit captures. Для получения дополнительной информации см. Прочие конструкции.For more information, see Miscellaneous Constructs.

  • Использовать параметр n в языковом элементе (?imnsx:subexpression).Use the n option in the (?imnsx:subexpression) language element. Этот параметр отключает все неименованные и неявные захваты в части выражения subexpression.This option disables all unnamed or implicit captures in subexpression. Захваты для всех вложенных неименованных и неявных групп также отключаются.Captures by any unnamed or implicit nested capturing groups are disabled as well.

К началуBack to top

ЗаголовокTitle ОПИСАНИЕDescription
Подробные сведения о поведении регулярных выраженийDetails of Regular Expression Behavior Описание реализации обработчика регулярных выражений в .NET.Examines the implementation of the regular expression engine in .NET. В этом разделе основное внимание уделено гибкости регулярных выражений; кроме того, рассказывается об ответственности разработчика за эффективную и надежную работу обработчика регулярных выражений.The topic focuses on the flexibility of regular expressions and explains the developer's responsibility for ensuring the efficient and robust operation of the regular expression engine.
Поиск с возвратомBacktracking Описание поиска с возвратом и того, как он влияет на производительность регулярных выражений. Описание языковых элементов, которые можно использовать вместо поиска с возвратом.Explains what backtracking is and how it affects regular expression performance, and examines language elements that provide alternatives to backtracking.
Элементы языка регулярных выражений — краткий справочникRegular Expression Language - Quick Reference Описание элементов языка регулярных выражений в .NET и ссылки на подробную документацию для каждого языкового элемента.Describes the elements of the regular expression language in .NET and provides links to detailed documentation for each language element.