Partager via


Meilleures pratiques des expressions régulières dans le .NET Framework

Le moteur des expressions régulières du .NET Framework est un outil puissant et complet. Il traite le texte en fonction de correspondances de modèle plutôt qu'en comparant et en faisant correspondre le texte littéral. Dans la plupart des cas, il exécute les critères spéciaux de façon rapide et efficace. Toutefois, dans certains cas, le moteur des expressions régulières peut sembler très lent. Dans des cas extrêmes, il semble même cesser de répondre. Il traite en effet peu d'entrées sur une période de plusieurs heures ou même de plusieurs jours.

Cette rubrique décrit quelques-unes des meilleures pratiques que les développeurs peuvent adopter afin de garantir que les expressions régulières atteignent des performances optimales. Elle contient les sections suivantes :

  • Prise en compte de la source d'entrée

  • Gestion correcte de l'instanciation d'objet

  • Prise en charge de la rétroaction

  • Capture uniquement lorsque cela s'avère nécessaire

  • Rubriques connexes

Prise en compte de la source d'entrée

En général, les expressions régulières peuvent accepter deux types d'entrée : avec contrainte ou sans contrainte. L'entrée avec contrainte est un texte provenant d'une source fiable ou connue, et qui suit un format prédéfini. L'entrée sans contrainte est un texte provenant d'une source non fiable, telle qu'un utilisateur web. Elle ne suit pas forcément un format prédéfini ou attendu.

Les modèles d'expressions régulières sont généralement écrits pour correspondre à une entrée valide. Autrement dit, les développeurs examinent le texte qu'ils souhaitent faire correspondre, puis ils écrivent un modèle d'expression régulière qui lui correspond. Les développeurs déterminent ensuite si ce modèle doit être corrigé ou approfondi en le testant à l'aide de plusieurs éléments d'entrée valides. Lorsque le modèle correspond à toutes les entrées valides supposées, il est déclaré prêt pour la production et peut être intégré à une application finale. Le modèle d'expression régulière est ainsi adapté à la mise en correspondance d'une entrée avec contrainte. Toutefois, il n'est pas adapté à la mise en correspondance d'une entrée sans contrainte.

Pour faire correspondre une entrée sans contrainte, une expression régulière doit pouvoir gérer efficacement trois types de texte :

  • Texte correspondant au modèle d'expression régulière.

  • Texte ne correspondant pas au modèle d'expression régulière.

  • Texte correspondant presque au modèle d'expression régulière.

Le dernier type de texte est problématique pour une expression régulière écrite pour gérer les entrées avec contrainte. Si cette expression régulière repose également sur une rétroaction complète, le traitement d'un texte apparemment anodin par le moteur des expressions régulières risque d'être extrêmement long (dans certains cas, un grand nombre d'heures ou de jours).

Prenons l'exemple d'une expression régulière très fréquemment utilisée, mais extrêmement problématique, pour la validation de l'alias d'une adresse de messagerie. L'expression régulière ^[0-9A-Z]([-.\w]*[0-9A-Z])*$ est écrite pour le traiter ce qui est considéré comme une adresse de messagerie valide. Cette dernière se compose d'un caractère alphanumérique ou d'un trait de soulignement, suivi de zéro, un ou plusieurs caractères (des caractères alphanumériques, des points, des traits de soulignement ou des traits d'union). L'expression régulière doit se terminer par un caractère alphanumérique ou un trait de soulignement. Toutefois, comme illustré dans l'exemple suivant, bien que cette expression régulière gère facilement une entrée valide, elle s'avère particulièrement inefficace lorsqu'il s'agit de traiter une entrée presque valide.

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

Comme le montre la sortie de l'exemple, le moteur des expressions régulières traite l'alias de messagerie valide dans un intervalle de temps à peu près identique, indépendamment de sa longueur. En revanche, lorsque l'adresse de messagerie presque valide comporte plus de cinq caractères, le temps de traitement est environ doublé pour chaque caractère supplémentaire de la chaîne. Cela signifie qu'une chaîne de 28 caractères presque valide serait traitée en plus d'une heure et qu'une chaîne de 33 caractères presque valide serait traitée en un peu moins d'un jour.

Étant donné que cette expression régulière a été développée en prenant uniquement en considération le format de l'entrée à faire correspondre, elle ne tient pas compte des entrées qui ne correspondent pas au modèle. Une entrée sans contrainte correspondant presque au modèle d'expression régulière risque ainsi de nuire considérablement aux performances.

Pour résoudre ce problème, vous pouvez effectuer les opérations suivantes :

  • Lorsque vous développez un modèle, vous devez réfléchir à la manière dont la rétroaction peut affecter les performances du moteur des expressions régulières, en particulier si votre expression régulière est conçue pour traiter des entrées sans contrainte. Pour plus d'informations, consultez la section Prise en charge de la rétroaction.

  • Testez intégralement votre expression régulière à l'aide d'entrées non valides et presque valides, ainsi que d'entrées valides. Pour générer de manière aléatoire une entrée pour une expression régulière spécifique, vous pouvez utiliser Rex (page éventuellement en anglais), l'outil d'exploration d'expressions régulières de Microsoft Research.

Gestion correcte de l'instanciation d'objet

La classe System.Text.RegularExpressions.Regex est au cœur du modèle d'objet d'expression régulière de .NET Framework. Elle représente le moteur des expressions régulières. Souvent, la façon dont le moteur Regex est utilisé est le facteur principal ayant un impact sur les performances des expressions régulières. La définition d'une expression régulière implique une association étroite entre le moteur des expressions régulières et un modèle d'expression régulière. Ce processus est forcément onéreux, qu'il implique l'instanciation d'un objet Regex en passant à son constructeur un modèle d'expression régulière ou l'appel d'une méthode statique en lui passant le modèle d'expression régulière avec la chaîne à analyser.

RemarqueRemarque

Pour une présentation détaillée des répercussions sur les performances de l'utilisation d'expressions régulières interprétées et compilées, consultez Optimisation des performances des expressions régulières, deuxième partie : Prise en charge de la rétroaction (éventuellement en anglais) sur le blog de l'équipe BCL.

Vous pouvez associer le moteur des expressions régulières à un modèle d'expression régulière spécifique, puis utiliser le moteur pour faire correspondre du texte de plusieurs façons :

  • Vous pouvez appeler une méthode statique de critères spéciaux, comme Regex.Match(String, String). L'instanciation d'un objet d'expression régulière n'est pas nécessaire.

  • Vous pouvez instancier un objet Regex et appeler une méthode d'instance de critères spéciaux d'une expression régulière interprétée. Il s'agit de la méthode par défaut pour lier le moteur des expressions régulières à un modèle d'expression régulière. Elle se produit lorsqu'un objet Regex est instancié sans argument options incluant l'indicateur Compiled.

  • Vous pouvez instancier un objet Regex et appeler une méthode d'instance de critères spéciaux d'une expression régulière compilée. Les objets d'expression régulière représentent des modèles compilés lorsqu'un objet Regex est instancié avec un argument options incluant l'indicateur Compiled.

  • Vous pouvez créer un objet Regex qui a un usage spécial et qui est fortement couplé à un modèle d'expression régulière particulier, le compiler et l'enregistrer dans un assembly autonome. Pour ce faire, appelez la méthode Regex.CompileToAssembly.

La façon dont vous appelez les méthodes de correspondance d'expression régulière peut avoir un impact significatif sur votre application. Les sections suivantes expliquent quand utiliser les appels de méthode statique, les expressions régulières interprétées et les expressions régulières compilées afin d'améliorer les performances de votre application.

Remarque importanteImportant

La forme de l'appel de méthode (statique, interprétée, compilée) affecte les performances si une même expression régulière est utilisée à plusieurs reprises dans les appels de méthode, ou si une application entraîne l'utilisation intensive d'objets d'expression régulière.

Expressions régulières statiques

Les méthodes d'expression régulière statiques sont recommandées pour éviter d'instancier à plusieurs reprises un objet d'expression régulière avec la même expression régulière. À la différence des modèles d'expressions régulières utilisés par les objets d'expression régulière, les codes d'opération ou le langage compilé MSIL (Microsoft intermediate langage) des modèles utilisés dans les appels de méthode d'instance sont mis en cache en interne par le moteur des expressions régulières.

Par exemple, un gestionnaire d'événements appelle fréquemment une autre méthode pour valider l'entrée d'utilisateur. Ceci se reflète dans le code suivant, dans lequel l'événement Click d'un contrôle Button est utilisé pour appeler une méthode nommée IsValidCurrency, qui vérifie si l'utilisateur a entré un symbole monétaire suivi d'au moins un chiffre décimal.

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

L'exemple suivant illustre une implémentation très peu efficace de la méthode IsValidCurrency. Notez que chaque appel de méthode réinstancie un objet Regex avec le même modèle. Cela signifie ainsi que le modèle d'expression régulière doit être recompilé chaque fois que la méthode est appelée.

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

Vous devez remplacer ce code peu efficace par un appel à la méthode statique Regex.IsMatch(String, String). De cette manière, un objet Regex n'a pas besoin d'être instancié chaque fois que vous souhaitez appeler une méthode de critères spéciaux. En outre, le moteur des expressions régulières est alors en mesure de récupérer une version compilée de l'expression régulière depuis son 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); 
   }
}

Par défaut, les 15 derniers modèles d'expressions régulières statiques utilisés récemment sont mis en cache. Pour les applications qui requièrent un plus grand nombre d'expressions régulières statiques mises en cache, la taille du cache peut être ajustée en définissant la propriété Regex.CacheSize.

L'expression régulière \p{Sc}+\s*\d+ utilisée dans cet exemple vérifie que la chaîne d'entrée se compose d'un symbole monétaire et d'au moins un chiffre décimal. Le modèle est défini comme indiqué dans le tableau suivant.

Modèle

Description

\p{Sc}+

Mettre en correspondance un ou plusieurs caractères dans la catégorie Unicode symbole, devise.

\s*

Correspond à zéro, un ou plusieurs espaces blancs.

\d+

Mettre en correspondance un ou plusieurs chiffres décimaux.

Expressions régulières interprétées ouExpressions régulières compilées

Les modèles d'expressions régulières qui ne sont pas associés au moteur des expressions régulières par la spécification de l'option Compiled sont interprétés. Lorsqu'un objet d'expression régulière est instancié, le moteur des expressions régulières convertit l'expression régulière en un ensemble de codes d'opération. Lorsqu'une méthode d'instance est appelée, les codes d'opération sont convertis en langage MSIL et exécutés par le compilateur JIT. De même, lorsqu'une méthode d'expression régulière statique est appelée et que l'expression régulière ne peut pas être récupérée dans le cache, le moteur des expressions régulières convertit l'expression régulière en un ensemble de codes d'opération et les stocke dans le cache. Il convertit ensuite ces codes d'opération en langage MSIL afin que le compilateur JIT puisse les exécuter. Les expressions régulières interprétées réduisent le temps de démarrage, mais ralentissent le temps d'exécution. Pour cette raison, il est préférable de les utiliser lorsque l'expression régulière est utilisée dans un nombre d'appels de méthode restreint, ou lorsque le nombre exact d'appels de méthodes d'expression régulière est inconnu, mais qu'il est supposé être petit. À mesure que le nombre d'appels de méthode augmente, le ralentissement de la vitesse d'exécution l'emporte sur l'amélioration des performances liée à la réduction du temps de démarrage.

Les modèles d'expressions régulières qui sont associés au moteur des expressions régulières par la spécification de l'option Compiled sont compilés. Cela signifie que, lorsqu'un objet d'expression régulière est instancié ou lorsqu'une méthode d'expression régulière statique est appelée et que l'expression régulière ne peut pas être récupérée dans le cache, le moteur des expressions régulières convertit l'expression régulière en un ensemble de codes d'opération intermédiaire, qu'il convertit ensuite en langage MSIL. Lorsqu'une méthode est appelée, le compilateur JIT exécute le MSIL. Contrairement aux expressions régulières interprétées, les expressions régulières compilées augmentent le temps de démarrage, mais elles exécutent plus rapidement les méthodes de critères spéciaux individuelles. En conséquence, l'amélioration des performances due à la compilation de l'expression régulière augmente en fonction du nombre de méthodes d'expression régulières appelées.

Pour résumer, nous vous conseillons d'utiliser des expressions régulières interprétées lorsque vous appelez relativement peu souvent des méthodes d'expression régulières avec une expression régulière spécifique. Nous vous conseillons d'utiliser des expressions régulières compilées lorsque vous appelez relativement souvent des méthodes d'expression régulière avec une expression régulière spécifique. Il est difficile de déterminer le seuil exact auquel le ralentissement de la vitesse d'exécution des expressions normales interprétées l'emporte sur la réduction du temps de démarrage. Il est également difficile de déterminer le seuil auquel le ralentissement du temps de démarrage des expressions régulières compilées l'emporte sur l'amélioration des vitesses d'exécution. Différents facteurs doivent être pris en compte, notamment la complexité des expressions régulières et les données spécifiques qui sont traitées. Pour déterminer si ce sont les expressions régulières interprétées ou compilées qui offrent les meilleures performances pour votre scénario d'application spécifique, vous pouvez utiliser la classe Stopwatch pour comparer les durées d'exécution.

L'exemple suivant compare les performances des expressions régulières compilées et celles des expressions régulières interprétés lors de la lecture des dix premières phrases et lors de la lecture de toutes les phrases de l'œuvre de Theodore Dreiser, Le financier. Comme l'indique la sortie de l'exemple, lorsque les méthodes de correspondance d'expression régulière sont appelées seulement dix fois, une expression régulière interprétée offre de meilleures performances qu'une expression régulière compilée. Par contre, une expression régulière compilée offre de meilleures performances dans le cas d'un grand nombre d'appels (dans le cas présent, plus de 13 000).

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

Le modèle d'expression régulière utilisé dans l'exemple, \b(\w+((\r?\n)|,?\s))*\w+[.?:;!], est défini comme indiqué dans le tableau suivant.

Modèle

Description

\b

Commencer la correspondance à la limite d'un mot.

\w+

Mettre en correspondance un ou plusieurs caractères alphabétiques.

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

Mettre en correspondance un ou aucun retour chariot suivi d'un caractère de saut de ligne, ou une ou aucune virgule, suivie d'un espace blanc.

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

Mettre en correspondance zéro, une ou plusieurs occurrences d'un ou plusieurs caractères alphabétiques qui sont suivis par un ou aucun retour chariot et un caractère de saut de ligne, ou par une ou aucune virgule, suivie d'un espace blanc.

\w+

Mettre en correspondance un ou plusieurs caractères alphabétiques.

[.?:;!]

Mettre en correspondance un point, un point d'interrogation, deux-points, un point-virgule ou un point d'exclamation.

Expressions régulières : compilées dans un assembly

Le .NET Framework vous permet également de créer un assembly qui contient des expressions régulières compilées. La baisse de performances des expressions régulières est alors ressentie au moment du design et non au moment de l'exécution. Toutefois, cela engendre également un travail supplémentaire : vous devez définir les expressions régulières à l'avance et les compiler dans un assembly. Le compilateur peut ensuite référencer cet assembly lors de la compilation du code source qui utilise les expressions régulières de l'assembly. Chaque expression régulière compilée dans l'assembly est représentée par une classe qui dérive de Regex.

Pour compiler des expressions régulières dans un assembly, vous appelez la méthode Regex.CompileToAssembly(RegexCompilationInfo[], AssemblyName) et vous lui passez un tableau d'objets RegexCompilationInfo représentant les expressions régulières à compiler, ainsi qu'un objet AssemblyName contenant des informations sur l'assembly à créer.

Nous vous conseillons de compiler les expressions régulières dans un assembly dans les situations suivantes :

  • Vous êtes un développeur de composants et vous souhaitez créer une bibliothèque d'expressions régulières réutilisables.

  • Vous savez que les méthodes de critères spéciaux de vos expression régulières seront appelées un nombre de fois indéterminé (entre une fois et des dizaines de milliers de fois). Contrairement aux expressions régulières compilées ou interprétées, les expressions régulières qui sont compilées dans des assemblys distincts offrent des performances homogènes, indépendamment du nombre d'appels de méthode.

Si vous utilisez des expressions régulières compilées pour optimiser les performances, vous ne devez pas utiliser la réflexion pour créer l'assembly, charger le moteur des expressions régulières et exécuter ses méthodes de critères spéciaux. Vous devez donc éviter de générer dynamiquement des modèles d'expressions régulières et spécifier les options de critères spéciaux (tels que des critères spéciaux de non respect de la casse) au moment de la création de l'assembly. Vous devez également séparer le code qui crée l'assembly du code qui utilise l'expression régulière.

L'exemple suivant montre comment créer un assembly qui contient une expression régulière compilée. Il crée un assembly nommé RegexLib.dll avec une classe d'expression régulière unique, SentencePattern, qui contient le modèle d'expression régulière de phrase correspondante utilisé dans la section Expressions régulières interprétées et expressions régulières compilées.

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

Lorsque l'exemple est compilé dans un exécutable et qu'il est exécuté, il crée un assembly nommé RegexLib.dll. L'expression régulière est représentée par une classe nommée Utilities.RegularExpressions.SentencePattern, dérivée de Regex. L'exemple suivant utilise ensuite l'expression régulière compilée pour extraire les phrases de l'œuvre de Theodore Dreiser, Le 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.

Prise en charge de la rétroaction

Normalement, le moteur des expressions régulières utilise une progression linéaire pour se déplacer dans une chaîne d'entrée et pour la comparer à un modèle d'expression régulière. Toutefois, lorsque les quantificateurs indéterminés, *, + et ?, par exemple, sont utilisés dans un modèle d'expression régulière, le moteur des expressions régulières peut abandonner une partie des correspondances partielles trouvées et revenir à un état précédemment enregistré pour trouver une correspondance pour le modèle entier. Ce processus est appelé « rétroaction ».

RemarqueRemarque

Pour plus d'informations sur la rétroaction, consultez Comportement détaillé des expressions régulières et Rétroaction.Pour obtenir une présentation détaillée de la rétroaction, consultez Optimisation des performances des expressions régulières, deuxième partie : Prise en charge de la rétroaction (éventuellement en anglais) sur le blog de l'équipe BCL.

La prise en charge de la rétroaction confère aux expressions régulières leur puissance et leur flexibilité. La responsabilité de contrôle du fonctionnement du moteur des expressions régulières est alors confiée aux développeurs d'expressions régulières. Souvent, les développeurs ne sont pas conscients de cette responsabilité. Leur utilisation incorrecte de la rétroaction ou leur dépendance vis-à-vis d'une rétroaction excessive a souvent un impact négatif très important sur les performances des expressions régulières. Dans le pire des scénarios, la durée d'exécution peut doubler pour chaque caractère supplémentaire de la chaîne d'entrée. En réalité, lorsque la rétroaction est utilisée de manière excessive, il est facile de créer l'équivalent de programmation d'une boucle sans fin si l'entrée correspond presque au modèle d'expression régulière. Le moteur des expressions régulières peut alors traiter une chaîne d'entrée relativement courte en plusieurs heures, voire en plusieurs jours.

Les performances des applications sont souvent altérées par l'utilisation de la rétroaction, même si ce processus n'est pas essentiel pour une correspondance. Par exemple, l'expression régulière \b\p{Lu}\w*\b établit une correspondance entre tous les mots qui commencent par une majuscule, comme indiqué dans le tableau suivant.

Modèle

Description

\b

Commencer la correspondance à la limite d'un mot.

\p{Lu}

Mettre en correspondance une majuscule.

\w*

Mettre en correspondance zéro, un ou plusieurs caractères alphabétiques.

\b

Terminer la correspondance à la limite d'un mot.

Étant donné qu'une limite de mot est différente d'un caractère alphabétique et qu'elle n'est pas un sous-ensemble de ce dernier, il est impossible que le moteur des expressions régulières franchisse une limite de mot lors de la mise en correspondance de caractères alphabétiques. Cela signifie que, pour cette expression régulière, une rétroaction ne peut jamais contribuer à la réussite globale d'une correspondance. Elle risque en revanche de diminuer les performances, étant donné que le moteur des expressions régulières doit impérativement enregistrer sont état pour chaque correspondance préliminaire d'un caractère alphabétique trouvée.

Si vous considérez que la rétroaction n'est pas nécessaire, vous pouvez la désactiver à l'aide de l'élément de langage (?>subexpression). L'exemple suivant analyse une chaîne d'entrée à l'aide de deux expressions régulières. La première, \b\p{Lu}\w*\b, utilise la rétroaction. La seconde, \b\p{Lu}(?>\w*)\b, désactive la rétroaction. Comme l'indique la sortie de l'exemple, les résultats obtenus sont identiques.

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

Dans de nombreux cas, la rétroaction est essentielle pour faire correspondre un modèle d'expression régulière à un texte d'entrée. Dans ces situations, une rétroaction excessive risque d'altérer considérablement les performances et de donner l'impression qu'une application a cessé de répondre. Cela se produit notamment lorsque les quantificateurs sont imbriqués et que le texte correspondant à la sous-expression externe est un sous-ensemble du texte correspondant à la sous-expression interne.

Par exemple, le modèle d'expression régulière ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$ est conçu pour correspondre à un numéro de référence composé d'au moins un caractère alphanumérique. Tous les caractères supplémentaires peuvent être composés d'un caractère alphanumérique, d'un trait d'union, d'un trait de soulignement ou d'un point. Toutefois, le dernier caractère doit impérativement être alphanumérique. Un signe dollar termine le numéro de référence. Dans certains cas, ce modèle d'expression régulière peut présenter des performances médiocres, si les quantificateurs sont imbriqués et que la sous-expression [0-9A-Z] est un sous-ensemble de la sous-expression [-.\w]*.

Dans ces cas, vous pouvez optimiser les performances des expressions régulières en supprimant les quantificateurs imbriqués et en remplaçant la sous-expression externe par une assertion de préanalyse ou de postanalyse de largeur nulle. Les assertions de préanalyse et de postanalyse sont des points d'ancrage. Elles ne déplacent pas le pointeur dans la chaîne d'entrée, mais elles vérifient en amont et en aval si une condition spécifiée est remplie. Par exemple, l'expression régulière de numéro de référence peut être ^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$. Ce modèle d'expression régulière est défini comme indiqué dans le tableau suivant.

Modèle

Description

^

Commencer la correspondance au début de la chaîne d'entrée.

[0-9A-Z]

Mettre en correspondance un caractère alphanumérique. Le numéro de référence doit au minimum comporter ce caractère.

[-. \w]*

Mettre en correspondance zéro, une ou plusieurs occurrences de tout caractère alphabétique, trait d'union ou point.

\$

Mettre en correspondance un signe dollar.

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

Effectuer une préanalyse avancée du signe dollar de fin de s'assurer que le caractère précédent est un caractère alphanumérique.

$

Terminer la correspondance à la fin de la chaîne d'entrée.

L'exemple suivant illustre l'utilisation de cette expression régulière pour faire correspondre un tableau pouvant contenir des numéros de référence potentiels.

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.

Le langage d'expression régulière dans le .NET Framework comprend les éléments de langage suivants, que vous pouvez utiliser pour éliminer les quantificateurs imbriqués. Pour plus d'informations, consultez Constructions de regroupement.

Élément du langage

Description

(?=subexpression)

Préanalyse positive de largeur nulle. Effectuer une préanalyse de la position actuelle pour déterminer si subexpression correspond à la chaîne d'entrée.

(?!subexpression)

Préanalyse négative de largeur nulle. Effectuer une préanalyse de la position actuelle pour déterminer si subexpression ne correspond pas à la chaîne d'entrée.

(?<=subexpression)

Postanalyse positive de largeur nulle. Effectuer une postanalyse de la position actuelle pour déterminer si subexpression correspond à la chaîne d'entrée.

(?<!subexpression)

Postanalyse négative de largeur nulle. Effectuer une postanalyse de la position actuelle pour déterminer si subexpression ne correspond pas à la chaîne d'entrée.

Capture uniquement lorsque cela s'avère nécessaire

Les expressions régulières du .NET Framework prennent en charge un certain nombre de constructions de regroupement, ce qui vous permet de regrouper un modèle d'expression régulière dans une ou plusieurs sous-expressions. Les constructions de regroupement les plus fréquemment utilisées dans le langage d'expressions régulières .NET Framework sont (subexpression), qui définit un groupe de capture numéroté et (?<name>subexpression), qui définit un groupe de capture nommé. Les constructions de regroupement sont essentielles pour créer des références arrières et pour définir une sous-expression à laquelle un quantificateur est appliqué.

Toutefois, l'utilisation de ces éléments de langage n'est pas sans effet. Ils entraînent le remplissage de l'objet GroupCollection renvoyé par la propriété Match.Groups avec les captures nommées ou sans nom les plus récentes. Si une seule construction de regroupement a capturé plusieurs sous-chaînes dans la chaîne d'entrée, elles remplissent également l'objet CaptureCollection renvoyé par la propriété Group.Captures d'un groupe de capture particulier à l'aide de plusieurs objets Capture.

Souvent, les constructions de regroupement sont utilisées dans une expression régulière uniquement pour que les quantificateurs puissent leur être appliqués. Les groupes capturés par ces sous-expressions ne sont alors pas utilisés par la suite. Par exemple, l'expression régulière \b(\w+[;,]?\s?)+[.?!] est conçue pour capturer une phrase entière. Le tableau suivant décrit les éléments de langage dans ce modèle d'expression régulière et leur effet sur les Match.Groups des objets Match et les collections Group.Captures.

Modèle

Description

\b

Commencer la correspondance à la limite d'un mot.

\w+

Mettre en correspondance un ou plusieurs caractères alphabétiques.

[;,]?

Mettre en correspondance zéro ou une virgule, ou zéro ou un point-virgule.

\s?

Mettre en correspondance zéro ou un espace blanc.

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

Mettre en correspondance une ou plusieurs occurrences d'un ou plusieurs caractères alphabétiques suivis d'une virgule ou d'un point-virgule facultatif suivi d'un espace blanc facultatif. Cela définit le premier groupe de capture. Il est nécessaire pour que la combinaison de plusieurs caractères alphabétiques (autrement dit, un mot) suivis d'un signe de ponctuation facultatif soit répétée jusqu'à ce que le moteur des expressions régulières ait atteint la fin d'une phrase.

[.?!]

Mettre en correspondance un point, un point d'interrogation ou un point d'exclamation.

Comme l'indique l'exemple suivant, lorsqu'une correspondance est trouvée, l'objet GroupCollection et l'objet CapturesCollection sont remplis avec des captures de la correspondance. Dans ce cas, le groupe de capture (\w+[;,]?\s?) existe afin que le quantificateur + puisse lui être appliqué, ce qui permet au modèle d'expression régulière de correspondre à chaque mot d'une phrase. Sinon, elle correspondrait au dernier mot d'une phrase.

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.

Lorsque vous utilisez des sous-expressions uniquement pour y appliquer des quantificateurs et que le texte capturé ne vous intéresse pas, vous devez désactiver les captures de groupe. Par exemple, l'élément de langage (?:subexpression) empêche le groupe auquel il s'applique de capturer les sous-chaînes correspondantes. Dans l'exemple suivant, le modèle d'expression régulière de l'exemple précédent est remplacé par \b(?:\w+[;,]?\s?)+[.?!]. Comme l'indique la sortie, le moteur des expressions régulières ne peut pas remplir les collections GroupCollection et CapturesCollection.

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.

Vous pouvez désactiver les captures de l'une des façons suivantes :

  • Utilisez l'élément de langage (?:subexpression). Cet élément empêche la capture des sous-chaînes correspondantes dans le groupe auquel il s'applique. Il ne désactive pas les captures de la sous-chaîne dans les groupes imbriqués.

  • Utilisez l'option ExplicitCapture. Elle désactive toutes les captures implicites ou sans nom dans le modèle d'expression régulière. Lorsque vous utilisez cette option, seules les sous-chaînes qui correspondent à des groupes nommés définis avec l'élément de langage (?<name>subexpression) peuvent être capturées. L'indicateur ExplicitCapture peut être passé au paramètre options d'un constructeur de classe Regex ou au paramètre options d'une méthode correspondante statique Regex.

  • Utilisez l'option n dans l'élément de langage (?imnsx). Cette option désactive toutes les captures implicites ou sans nom à partir du point où l'élément apparaît dans le modèle d'expression régulière. Les captures sont désactivées jusqu'à la fin du modèle ou jusqu'à ce que l'option (-n) active les captures implicites ou sans nom. Pour plus d'informations, consultez Constructions diverses.

  • Utilisez l'option n dans l'élément de langage (?imnsx:subexpression). Cette option désactive toutes les captures implicites ou sans nom dans subexpression. Les captures effectuées par les groupes de capture imbriqués implicites ou sans nom sont également désactivées.

Rubriques connexes

Titre

Description

Comportement détaillé des expressions régulières

Aborde l'implémentation du moteur des expressions régulières dans le .NET Framework. Cette rubrique traite de la flexibilité des expressions régulières. Elle explique la responsabilité du développeur pour que le fonctionnement efficace et fiable du moteur des expressions régulières soit garanti.

Rétroaction

Aborde la rétroaction et la façon dont elle affecte les performances des expressions régulières, ainsi que les éléments de langage, qui offrent des alternatives à la rétroaction.

Éléments du langage des expressions régulières

Décrit les éléments de langage des expressions régulières dans .NET Framework et propose des liens vers la documentation détaillée pour chaque élément de langage.

Historique des modifications

Date

Historique

Motif

Mars 2011

Ajout d'une rubrique.

Améliorations apportées aux informations.