Share via


Práticas recomendadas para expressões regulares na.NET Framework

O mecanismo de expressões regulares na.NET Framework é uma ferramenta poderosa e completa que processa o texto com base em padrões correspondentes, em vez de fazê-lo em Comparar e correspondência de texto literal. Na maioria dos casos, ele executa a correspondência de padrões, com rapidez e eficiência. No entanto, em alguns casos, o mecanismo de expressões regulares pode parecer muito lenta. Em casos extremos, ele poderá aparecer mesmo parar de responder enquanto ela processa uma entrada relativamente pequena no decorrer de horas ou mesmo dias.

Este tópico descreve algumas das práticas recomendadas que os desenvolvedores podem adotar para garantir que suas expressões regulares atingir o desempenho ideal. Ele contém as seguintes seções:

  • Considere a origem de entrada

  • Tratar a instanciação de objeto adequadamente

  • Cuide do Backtracking

  • Capturar somente quando necessário

  • Tópicos relacionados

Considere a origem de entrada

Em geral, expressões regulares podem aceitar os dois tipos de entrada: restrito ou ilimitado. Entrada restrita é o texto que se origina de uma fonte conhecida ou confiável e segue um formato predefinido. A entrada irrestrita é o texto que se origina de uma fonte não confiável, como, por exemplo, um usuário da web e não pode seguir um formato predefinido ou esperado.

Normalmente, os padrões de expressões regulares são gravados para corresponder à entrada válida. Ou seja, os desenvolvedores examinam o texto que deseja corresponder e então escrever um padrão de expressão regular que coincida com ele. Os desenvolvedores, então, determinar se esse padrão requer correção ou ainda mais a elaboração, testando-o com vários itens de entrada válidos. Quando o padrão corresponde a todas as entradas válidas de presumed, ele é declarado estar pronto para produção e pode ser incluído em um aplicativo lançado. Isso torna o padrão de expressão regular adequado para a correspondência de entrada restrita. No entanto, ele não torná-lo adequado para correspondência de entrada irrestrita.

Para corresponder à entrada irrestrita, uma expressão regular deve ser capaz de lidar eficientemente com três tipos de texto:

  • Texto que corresponda ao padrão de expressão regular.

  • Texto que não corresponde ao padrão de expressão regular.

  • Texto que corresponde a quase o padrão de expressão regular.

O último tipo de texto é especialmente problemático para uma expressão regular que foi escrita para lidar com entrada restrita. Se essa expressão regular também conta com amplos backtracking, o mecanismo de expressão regular pode gastar uma quantidade excessiva de tempo (em alguns casos, muitas horas ou dias) processar texto aparentemente inofensivo.

Por exemplo, considere uma expressão regular para validar o alias de um endereço de e-mail muito comumente usada, mas extremamente problemática. A expressão regular ^[0-9A-Z]([-.\w]*[0-9A-Z])*$ é gravado para processar o que é considerado um endereço de e-mail válido, que consiste em um caractere alfanumérico ou sublinhado, seguido por zero ou mais caracteres que podem ser alfanuméricos, períodos, sublinhados, ou hífens. A expressão regular deve terminar com um caractere alfanumérico ou sublinhado. No entanto, como mostra o exemplo a seguir, embora essa expressão regular manipula a entrada válida com facilidade, seu desempenho é muito ineficiente quando está processando a entrada válida quase.

Imports System.Diagnostics
Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim sw As Stopwatch    
      Dim addresses() As String = { "AAAAAAAAAAA@anyco.com", 
                                 "AAAAAAAAAAaaaaaaaaaa!@anyco.com" }
      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
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      Stopwatch sw;    
      string[] addresses = { "AAAAAAAAAAA@anyco.com", 
                             "AAAAAAAAAAaaaaaaaaaa!@anyco.com" };
      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

Como a saída do exemplo mostra, o mecanismo de expressão regular processa o alias de email válido em sobre o mesmo intervalo de tempo, independentemente de seu comprimento. Por outro lado, quando o endereço de e-mail válido quase tem mais de cinco caracteres, o tempo de processamento aproximadamente dobra para cada caractere adicional na seqüência de caracteres. Isso significa que uma seqüência de caracteres de 28 quase válida levará mais de uma hora para processar e uma seqüência de caracteres de 33 a quase válida levaria aproximadamente um dia para processar.

Porque essa expressão regular foi desenvolvido considerando apenas o formato de entrada a ser correspondido, ele não leva em conta a entrada que não correspondem ao padrão. Isso, por sua vez, pode permitir que a entrada irrestrita que quase corresponde ao padrão de expressão regular para degradar o desempenho significativamente.

Para resolver esse problema, você pode fazer o seguinte:

  • Ao desenvolver um padrão, você deve considerar como backtracking pode afetar o desempenho do mecanismo de expressões regulares, especialmente se a sua expressão regular é projetada para processar a entrada irrestrita. Para obter mais informações, consulte o Assumir encargo de Backtracking seção.

  • Teste sua expressão regular, usando a entrada do próxima válida e inválida, bem como a entrada válida. Para gerar a entrada para uma expressão regular aleatoriamente, você pode usar Rex, que é uma ferramenta de exploração de expressão regular da Microsoft Research.

Tratar a instanciação de objeto adequadamente

O cerne da.Modelo de objeto de expressão regular do NET Framework é o System.Text.RegularExpressions.Regex classe, que representa o mecanismo de expressão regular. Freqüentemente, o único fator maior que afeta o desempenho de expressão regular é a maneira na qual o Regex mecanismo é usado. A definição de uma expressão regular envolve o acoplamento fortemente o mecanismo de expressões regulares com um padrão de expressão regular. Que processo de união, se ela envolve instanciar um Regex , passando um padrão de expressão regular de seu construtor de objeto ou chamar um método estático, passando-o padrão de expressão regular, juntamente com a seqüência de caracteres a serem analisados, é pela necessidade de um caro.

Observação

Para uma discussão mais detalhada sobre as implicações de desempenho do uso de compilados e interpretadas de expressões regulares, consulte Otimizando o desempenho de expressão Regular, parte II: Tirando o encargo de Backtracking no blog da equipe da BCL.

Você pode acoplar o mecanismo de expressão regular com um padrão de expressão regular e use o mecanismo para coincidir com o texto de várias maneiras:

  • Você pode chamar um método estático da correspondência de padrões, como Regex.Match(String, String). Isso não exige a instanciação de um objeto de expressão regular.

  • Você pode instanciar um Regex de objeto e chamar um método de correspondência de padrões de instância de uma expressão regular interpretada. Este é o método padrão para o mecanismo de expressão regular de ligação a um padrão de expressão regular. Ele resulta quando uma Regex objeto é instanciado sem um options argumento que inclui o Compiled sinalizador.

  • Você pode instanciar um Regex de objeto e chamar um método de correspondência de padrões de instância de uma expressão regular compilado. Objetos de expressão regular representam compilado padrões quando um Regex objeto é instanciado com um options argumento que inclui o Compiled sinalizador.

  • Você pode criar uma finalidade especial Regex objeto que está intimamente ligado com um padrão de expressão regular, compilá-lo e salvá-lo para um assembly independente. Faça isso chamando o Regex.CompileToAssembly método.

A forma específica em que você chamar os métodos de correspondência da expressão regular pode ter um impacto significativo no seu aplicativo. As seções a seguir abordam quando usar static método chama, interpretado de expressões regulares e compilado de expressões regulares para melhorar o desempenho do aplicativo.

Observação importanteImportante

A forma do método call (estático, interpretado, compilado) afeta o desempenho se a mesma expressão regular é usada repetidamente em chamadas de método, ou se um aplicativo faz uso extensivo de objetos de expressão regular.

Estáticas expressões regulares

Os métodos estáticos de expressões regulares são recomendados como uma alternativa para repetidamente instanciar um objeto de expressão regular com a mesma expressão regular. Ao contrário de padrões de expressões regulares usadas pelos objetos de expressão regular, os códigos de operação ou o compilado Microsoft intermediate language (MSIL) a partir de padrões usados nas chamadas de método de instância é armazenada em cache internamente pelo mecanismo de expressão regular.

Por exemplo, um manipulador de eventos chama com freqüência o outro método para validar a entrada do usuário. Isso é refletido no código a seguir, no qual um Button do controle Click evento é usado para chamar um método denominado IsValidCurrency, que verifica se o usuário digitou um símbolo de moeda, seguido pelo menos um dígito decimal.

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
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.";
}

Uma implementação muito ineficiente a IsValidCurrency método é mostrado no exemplo a seguir. Observe que cada chamada do método reinstantiates um Regex o objeto com o mesmo padrão. Isso, por sua vez, significa que o padrão de expressão regular deve ser recompilado cada vez que o método é chamado.

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
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);
   }
}

Você deve substituir esse código ineficiente uma chamada a um estático Regex.IsMatch(String, String) método. Isso elimina a necessidade de criar uma instância de um Regex objeto cada vez que você deseja chamar um método de correspondência de padrões e permite que o mecanismo de expressões regulares recuperar uma versão compilada da expressão regular do cache.

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
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); 
   }
}

Por padrão, os últimos 15 usados mais recentemente estático expressão regular, padrões são armazenados em cache. Para aplicativos que exigem um número maior de que as expressões regulares em cache estáticos, o tamanho do cache pode ser ajustado, definindo a Regex.CacheSize propriedade.

A expressão regular \p{Sc}+\s*\d+ que é usada neste exemplo verifica que a seqüência de caracteres de entrada consiste em um símbolo de moeda e pelo menos um dígito decimal. O padrão é definido como mostrado na tabela a seguir.

Padrão

Descrição

\p{Sc}+

Corresponde a um ou mais caracteres em Unicode símbolo, a categoria de moeda.

\s*

Corresponde a zero ou mais caracteres de espaço em branco.

\d+

Corresponde a um ou mais dígitos decimais.

Interpretado vs.Expressões regulares compilados

Padrões de expressões regulares que não estão ligados ao mecanismo por meio da especificação de expressão regular a Compiled opção são interpretados. Quando um objeto de expressão regular é instanciado, o mecanismo de expressão regular converte a expressão regular para um conjunto de códigos de operação. Quando um método de instância é chamado, os códigos de operação são convertidas em MSIL e executados pelo compilador JIT. Da mesma forma, quando um método estático de expressão regular é chamado e a expressão regular não pode ser encontrada no cache, o mecanismo de expressão regular converte a expressão regular para um conjunto de códigos de operação e as armazena no cache. Ela então converte esses códigos de operação para MSIL para que o compilador JIT pode executá-los. Expressões regulares de interpretada reduzem o tempo de inicialização ao custo de tempo de execução mais lento. Dessa forma, eles são melhor utilizados quando a expressão regular é usada em um pequeno número de chamadas de método, ou se o número exato de chamadas para métodos de expressão regular é desconhecido, mas deverá ser pequeno. Como o número de aumentos de chamadas de método, o ganho de desempenho do tempo de inicialização reduzido é outstripped pela velocidade de execução mais lenta.

Os padrões de expressões regulares são vinculados ao mecanismo por meio da especificação de expressão regular a Compiled opção são compilados. Isso significa que, quando um objeto de expressão regular é instanciado, ou quando um método estático de expressão regular é chamado e a expressão regular não pode ser encontrada no cache, o mecanismo de expressão regular converte a expressão regular para um conjunto intermediário de códigos de operação, o que, em seguida, converte MSIL. Quando um método é chamado, o compilador JIT executa o MSIL. Em contraste com interpretada expressões regulares, compiladas de expressões regulares aumentam o tempo de inicialização, mas executar os métodos de correspondência de padrões individuais com mais rapidez. Como resultado, o benefício de desempenho que resulta da compilação de expressão regular aumenta em proporção ao número de chamadas de métodos de expressão regular.

Para resumir, recomendamos que você use interpretadas expressões regulares, quando você chamar os métodos de expressão regular com uma expressão regular específica relativamente pouco. Quando você chamar os métodos de expressão regular com uma expressão regular específica relativamente com freqüência, você deve usar compiladas expressões regulares. O limite exato em que as velocidades de execução de expressões regulares de interpretada superem ganhos de seu tempo reduzido de inicialização ou o limite em que os tempos de inicialização mais lentos de compilado expressões regulares superam os ganhos de suas velocidades de execução mais rápidas, é difícil determinar. Ele depende de vários fatores, inclusive a complexidade da expressão regular e os dados específicos que processa. Para determinar se interpretada ou compilada expressões regulares oferecem o melhor desempenho para seu cenário de aplicativo específico, você pode usar o Stopwatch classe para comparar seus tempos de execução.

O exemplo a seguir compara o desempenho de expressões regulares compiladas e interpretadas ao ler as dez primeiras sentenças e ao ler todas as frases no texto de Theodore Dreiser A Financier. Como mostra a saída do exemplo, quando apenas dez chamadas são feitas a expressão regular a correspondência de métodos, uma expressão regular interpretada oferece desempenho melhor do que uma expressão regular compilado. No entanto, uma expressão regular compilada oferece o melhor desempenho quando um grande número de chamadas (nesse caso, a mais de 13.000) é feito.

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

O padrão de expressão regular usado no exemplo, \b(\w+((\r?\n)|,?\s))*\w+[.?:;!], que é definido como mostrado na tabela a seguir.

Padrão

Descrição

\b

Começa a correspondência de um limite de palavra.

\w+

Corresponde a um ou mais caracteres do word.

(\r? \n)|,? \s)

Corresponde a zero ou um retorno de carro seguido por um caractere de nova linha ou a zero ou uma vírgula, seguido por um caractere de espaço em branco.

(\w+((\r? \n)|,? \s))*

Corresponde a zero ou mais ocorrências de um ou mais caracteres de palavra são seguidas por zero ou um retorno de carro e um caractere de nova linha ou por zero ou uma vírgula, seguido por um caractere de espaço em branco.

\w+

Corresponde a um ou mais caracteres do word.

[.?:;!]

Corresponde a um período, ponto de interrogação, dois-pontos, ponto e vírgula ou ponto de exclamação.

Expressões regulares: Compiladas em um Assembly

A.NET Framework permite que você crie um assembly que contém o compilado expressões regulares. Isso move o impacto no desempenho da compilação de expressão regular de tempo de execução para o tempo de design. No entanto, ele também envolve algum trabalho adicional: Você deve definir as expressões regulares com antecedência e compilá-los a um assembly. O compilador pode então consultar este assembly durante a compilação de código-fonte que usa expressões regulares do assembly. Cada expressão regular compilado no assembly é representado por uma classe que deriva de Regex.

Para compilar as expressões regulares para um assembly, você chamar o Regex.CompileToAssembly(RegexCompilationInfo[], AssemblyName) método e passar uma matriz de RegexCompilationInfo objetos que representam as expressões regulares para ser compilado, e um AssemblyName objeto que contém informações sobre o assembly a ser criado.

Recomendamos que você compilar expressões regulares para um assembly nas seguintes situações:

  • Se você for um desenvolvedor de componentes que deseja criar uma biblioteca reutilizável expressões regulares.

  • Se você espera que a correspondência de padrões métodos da sua expressão regular sejam chamados um número indeterminado de vezes – em qualquer lugar de uma ou duas vezes para milhares ou dezenas de milhares de vezes. Ao contrário de expressões regulares compiladas ou interpretadas, expressões regulares que sejam compiladas em conjuntos separados oferecem desempenho consistente, independentemente do número de chamadas de método.

Se você estiver usando expressões regulares de compilado para otimizar o desempenho, você não deve usar a reflexão para criar o assembly, o mecanismo de expressão regular de carregar e executar seus métodos de correspondência de padrões. Isso requer que você a evitar a criação de padrões de expressões regulares dinamicamente e que você especifique as opções de correspondência de padrões (como a correspondência de padrão de maiúsculas e minúsculas) no momento o assembly é criado. Ele também requer que você separe o código que cria o assembly do código que usa a expressão regular.

O exemplo a seguir mostra como criar um assembly que contém uma expressão regular compilada. Ele cria um assembly chamado RegexLib.dll com uma classe única expressão regular, SentencePattern, que contém a expressão regular com correspondência de frase padrão usado na Interpreted vs. Compilado expressões regulares seção.

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
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);
   }
}

Quando o exemplo é compilado para um executável e a execução, ele cria um assembly chamado RegexLib.dll. A expressão regular é representada por uma classe chamada Utilities.RegularExpressions.SentencePattern que é derivada de Regex. O exemplo a seguir usa a expressão regular compilada para extrair as sentenças de texto do de Theodore Dreiser A Financier.

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

Cuide do Backtracking

Normalmente, o mecanismo de expressão regular usa progressão linear para percorrer uma seqüência de caracteres de entrada e compará-lo com um padrão de expressão regular. No entanto, quando indeterminados quantificadores, como *, +, e ? são usados em um padrão de expressão regular, o mecanismo de expressão regular pode dar a uma parte da bem-sucedida correspondências parciais e retornar a um estado salvo anteriormente para procurar uma correspondência com êxito para o padrão de todo. Esse processo é conhecido como backtracking.

Observação

Para obter mais informações sobre o backtracking, consulte Detalhes do comportamento de expressões regulares e Retrocedendo.Para obter uma discussão detalhada de backtracking, consulte Otimizando o desempenho de expressão Regular, parte II: Tirando o encargo de Backtracking no blog da equipe da BCL.

Suporte para backtracking proporciona a flexibilidade e capacidade de expressões regulares. Ele também coloca a responsabilidade de controlar a operação do mecanismo de expressão regular nas mãos dos desenvolvedores de expressão regular. Como os desenvolvedores normalmente não são cientes desta responsabilidade, seu uso indevido de backtracking ou dependência excessiva de backtracking costuma desempenha a função mais significativa em comprometer o desempenho de expressão regular. Em um cenário de pior caso, pode dobrar o tempo de execução para cada caractere adicional em seqüência de entrada. Na verdade, usando o backtracking excessivamente, é fácil criar o equivalente programático de um loop infinito, se a entrada corresponde quase o padrão de expressão regular; o mecanismo de expressão regular pode levar horas ou mesmo dias para processar uma seqüência de caracteres de entrada relativamente curta.

Freqüentemente, aplicativos pagam uma penalidade de desempenho para o uso de backtracking apesar do fato de que a backtracking não é essencial para uma correspondência. Por exemplo, a expressão regular \b\p{Lu}\w*\b corresponde a todas as palavras que começam com um caractere maiúsculo, como a tabela a seguir mostra.

Padrão

Descrição

\b

Começa a correspondência de um limite de palavra.

\p{Lu}

Corresponde a um caractere maiúsculo.

\w*

Corresponde a zero ou mais caracteres do word.

\b

Finalize a correspondência de um limite de palavra.

Como um limite de palavra não é igual, ou um subconjunto de, um caractere de palavra, não há nenhuma possibilidade de que o mecanismo de expressão regular irá cruzar um limite de palavra, ao fazer a correspondência de caracteres alfabéticos. Isso significa que para essa expressão regular, backtracking pode nunca contribuir para o sucesso de qualquer correspondência - ele pode degradar apenas o desempenho, porque o mecanismo de expressão regular é forçado para salvar seu estado para cada correspondência correta de preliminar de um caractere de palavra.

Se você determinar que backtracking não é necessário, você pode desativá-lo usando o (?>subexpression) elemento de linguagem. O exemplo a seguir analisa uma seqüência de caracteres de entrada usando duas expressões regulares. A primeira, \b\p{Lu}\w*\b, depende de backtracking. O segundo, \b\p{Lu}(?>\w*)\b, desativa o backtracking. Como a saída do exemplo mostra, ambos produzem o mesmo resultado.

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

Em muitos casos, é essencial para que corresponda a um padrão de expressão regular para o texto de entrada backtracking. Nesses casos, backtracking excessiva pode seriamente degradar o desempenho e criar a impressão de que um aplicativo parou de responder. Em particular, isso acontece quando quantificadores estão aninhados e o texto que corresponda a subexpressão externa é um subconjunto do texto que corresponda a subexpressão interna.

Por exemplo, o padrão de expressão regular ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$ destina-se para coincidir com um número de peça que consiste pelo menos um caractere alfanumérico. Quaisquer caracteres adicionais podem consistir de um caractere alfanumérico, um hífen, um sublinhado ou um período, embora o último caractere deve ser alfanumérico. Um sinal de cifrão finaliza o número de peça. Em alguns casos, o padrão de expressão regular pode apresentar um desempenho extremamente fraco porque quantificadores aninhadas e porque a subexpressão [0-9A-Z] é um subconjunto da subexpressão [-.\w]*.

Nesses casos, você pode otimizar o desempenho de expressão regular removendo os quantificadores aninhados e substituindo a subexpressão externa com uma largura zero lookahead ou declaração de lookbehind. Declarações de visão antecipada e lookbehind são como âncoras; Não mova o ponteiro na seqüência de entrada mas em vez disso, olhar à frente ou atrás verificar se uma condição especificada for satisfeita. Por exemplo, a expressão regular a parte numérica pode ser regravada como ^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$. O padrão de expressão regular é definido como mostrado na tabela a seguir.

Padrão

Descrição

^

Começa a correspondência no início da seqüência de entrada.

[0-9A-Z]

Corresponde a um caractere alfanumérico. O número de peça deve conter pelo menos esse caractere.

[-. \w]*

Corresponde a zero ou mais ocorrências de qualquer caractere de palavra, um hífen ou um período.

\$

Coincide com um sinal de cifrão.

(?<=[0-9A-Z])

Procure à frente do cifrão final para garantir que o caractere anterior seja alfanumérico.

$

Finalize a correspondência no final da seqüência de entrada.

O exemplo a seguir ilustra o uso dessa expressão regular para corresponder a uma matriz contendo números de peça possíveis.

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

A linguagem de expressão regular na.NET Framework inclui os seguintes elementos de linguagem que você pode usar para eliminar quantificadores aninhados. Para obter mais informações, consulte Agrupando Construtores.

Elemento de linguagem

Descrição

(?=subexpression)

Lookahead positivo de largura igual a zero. Procure à frente da posição atual para determinar se subexpression coincide com a seqüência de caracteres de entrada.

(?!subexpression)

Lookahead negativo de largura igual a zero. Procure à frente da posição atual para determinar se subexpression não coincide com a seqüência de caracteres de entrada.

(?<=subexpression)

Lookbehind positiva de largura igual a zero. Procure por trás da posição atual para determinar se subexpression coincide com a seqüência de caracteres de entrada.

(?<!subexpression)

Lookbehind negativo de largura igual a zero. Procure por trás da posição atual para determinar se subexpression não coincide com a seqüência de caracteres de entrada.

Capturar somente quando necessário

Expressões regulares na.NET Framework oferecem suporte a um número de construções de agrupamento, que permitem que você agrupe a subexpressões um ou mais de um padrão de expressão regular. A mais comumente usada construções de agrupamento na.Linguagem de expressão regular do NET Framework são (subexpressão), que define um grupo de captura numerado e (?<nome>subexpressão), que define um grupo nomeado de captura. Construções de agrupamento são essenciais para a criação de referências anteriores e para a definição de uma subexpressão aos quais um quantificador é aplicado.

No entanto, o uso desses elementos de linguagem tem um custo. Que causem o GroupCollection objeto retornado pela Match.Groups sem nome de propriedade a ser preenchido com o mais recente ou chamado capturas e se uma construção de agrupamento único capturou várias substrings na seqüência de entrada, eles também preencher o CaptureCollection objeto retornado pela Group.Captures a propriedade de um determinado grupo de captura com várias Capture objetos.

Construções de agrupamento são freqüentemente usadas em uma expressão regular somente para que quantificadores podem ser aplicados a eles e os grupos capturados por esses subexpressões não serão usados posteriormente. Por exemplo, a expressão regular \b(\w+[;,]?\s?)+[.?!] foi projetado para capturar uma frase inteira. A tabela a seguir descreve os elementos de linguagem em que o padrão de expressão regular e seu efeito sobre o Match do objeto Match.Groups e Group.Captures coleções.

Padrão

Descrição

\b

Começa a correspondência de um limite de palavra.

\w+

Corresponde a um ou mais caracteres do word.

[;,]?

Corresponde a zero ou uma vírgula ou ponto e vírgula.

\s?

Corresponde a zero ou um caractere de espaço em branco.

(\w+[;,]? \s?)+

Corresponde a uma ou mais ocorrências de um ou mais caracteres alfabéticos, seguidos por uma vírgula opcional ou -e-vírgula seguido por um caractere de espaço em branco opcional. Isso define o primeiro grupo de capturando, que é necessário para que a combinação de vários caracteres de palavra (ou seja, uma palavra) seguida por um símbolo de pontuação opcional será repetida até que o mecanismo de expressão regular atinge o final de uma frase.

[.?!]

Corresponde a um período, um ponto de interrogação ou um ponto de exclamação.

Como mostra o exemplo seguinte, quando uma correspondência for encontrada, ambos os GroupCollection e CapturesCollection objetos são preenchidos com capturas de correspondência. Nesse caso, o grupo de captura (\w+[;,]?\s?) existe para que o + quantificador pode ser aplicada a ele, que permite que o padrão de expressão regular coincidir com cada palavra na frase. Caso contrário, ele corresponderia a última palavra em uma frase.

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

Quando você usa o subexpressões apenas para aplicar quantificadores, e você não estiver interessado no texto capturado, você deve desabilitar capturas de grupo. Por exemplo, o (?:subexpression) o elemento de linguagem impede que o grupo ao qual ele se aplica Capture substrings correspondentes. No exemplo a seguir, o padrão de expressão regular do exemplo anterior é alterado para \b(?:\w+[;,]?\s?)+[.?!]. Como mostra a saída, ele impede o mecanismo de expressão regular preenchendo o GroupCollection e CapturesCollection coleções.

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

Você pode desabilitar a captura em uma das seguintes maneiras:

  • Use o (?:subexpression) elemento de linguagem. Este elemento impede que a captura de substrings correspondentes no grupo ao qual ele se aplica. Ela não desativa capturas de subseqüência em grupos aninhados.

  • Use o ExplicitCapture opção. Ela desabilita todas as capturas sem nome ou implícitas no padrão de expressão regular. Quando você usa essa opção, somente substrings correspondentes aos chamado grupos definidos com o (?<name>subexpression) elemento de linguagem que pode ser capturado. O ExplicitCapture sinalizador pode ser passado para o options parâmetro de um Regex Construtor de classe ou para o options parâmetro de um Regex estático método correspondente.

  • Use o n opção na (?imnsx) elemento de linguagem. Esta opção desativa todas as capturas sem nome ou implícitas do ponto no padrão de expressão regular, no qual o elemento é exibido. Capturas estão desativadas ou até o final do padrão ou até que o (-n) opção permite capturas sem nome ou implícito. Para obter mais informações, consulte Construtores Diversos.

  • Use o n opção na (?imnsx:subexpression) elemento de linguagem. Esta opção desativa todas as capturas sem nome ou implícitas, em subexpression. Capturas de quaisquer grupos de captura aninhados sem nome ou implícitas são desativadas bem.

Tópicos relacionados

Título

Descrição

Detalhes do comportamento de expressões regulares

Examina a implementação do mecanismo de expressão regular na.NET Framework. O tópico foca a flexibilidade de expressões regulares e explica a responsabilidade do desenvolvedor para garantir a operação eficiente e robusta do mecanismo de expressão regular.

Retrocedendo

Explica que backtracking é e como ele afeta o desempenho de expressão regular e, em seguida, examina os elementos de linguagem que oferecem alternativas à backtracking.

Elementos de linguagem das expressões regulares

Descreve os elementos da linguagem de expressão regular na.NET Framework e fornece links para documentação detalhada para cada elemento de linguagem.

Histórico de alterações

Date

History

Motivo

Março de 2011

Tópico adicional.

Aprimoramento de informações.