2009-12-18 3 views
0

Récemment, j'ai trouvé une API C# Regex très ennuyante.Pourquoi Group.Value toujours la dernière chaîne de groupe correspondante?

J'ai l'expression régulière (([0-9]+)|([a-z]+))+. Je veux trouver toute la chaîne assortie. Le code est comme ci-dessous.

string regularExp = "(([0-9]+)|([a-z]+))+"; 
string str = "abc123xyz456defFOO"; 

Match match = Regex.Match(str, regularExp, RegexOptions.None); 
int matchCount = 0; 

while (match.Success) 
{ 
    Console.WriteLine("Match" + (++matchCount)); 

    Console.WriteLine("Match group count = {0}", match.Groups.Count); 
    for (int i = 0; i < match.Groups.Count; i++) 
    { 
     Group group = match.Groups[i]; 
     Console.WriteLine("Group" + i + "='" + group.Value + "'"); 
    } 

    match = match.NextMatch(); 
    Console.WriteLine("go to next match"); 
    Console.WriteLine(); 
} 

La sortie est:

Match1 
Match group count = 4 
Group0='abc123xyz456def' 
Group1='def' 
Group2='456' 
Group3='def' 
go to next match 

Il semble que tout group.Value est la dernière chaîne adapté ("DEF" et "456"). J'ai passé un peu de temps à comprendre que je devrais compter sur le groupe.Captures au lieu de groupe.Valeur.

string regularExp = "(([0-9]+)|([a-z]+))+"; 
string str = "abc123xyz456def"; 
//Console.WriteLine(str); 

Match match = Regex.Match(str, regularExp, RegexOptions.None); 
int matchCount = 0; 

while (match.Success) 
{ 
    Console.WriteLine("Match" + (++matchCount)); 

    Console.WriteLine("Match group count = {0}", match.Groups.Count); 
    for (int i = 0; i < match.Groups.Count; i++) 
    { 
     Group group = match.Groups[i]; 
     Console.WriteLine("Group" + i + "='" + group.Value + "'"); 

     CaptureCollection cc = group.Captures; 
     for (int j = 0; j < cc.Count; j++) 
     { 
      Capture c = cc[j]; 
      System.Console.WriteLine(" Capture" + j + "='" + c + "', Position=" + c.Index); 
     } 
    } 

    match = match.NextMatch(); 
    Console.WriteLine("go to next match"); 
    Console.WriteLine(); 
} 

Affichera:

Match1 
Match group count = 4 
Group0='abc123xyz456def' 
    Capture0='abc123xyz456def', Position=0 
Group1='def' 
    Capture0='abc', Position=0 
    Capture1='123', Position=3 
    Capture2='xyz', Position=6 
    Capture3='456', Position=9 
    Capture4='def', Position=12 
Group2='456' 
    Capture0='123', Position=3 
    Capture1='456', Position=9 
Group3='def' 
    Capture0='abc', Position=0 
    Capture1='xyz', Position=6 
    Capture2='def', Position=12 
go to next match 

Maintenant, je me demande pourquoi la conception de l'API est comme ça. Pourquoi Group.Value ne renvoie que la dernière chaîne correspondante? Ce design n'a pas l'air bien.

Répondre

2

La raison principale est historique: les expressions rationnelles ont toujours fonctionné de cette façon, en remontant vers Perl et au-delà. Mais ce n'est pas vraiment un mauvais design. Habituellement, si vous voulez que chaque match comme ça, vous laissez juste le quantificateur le plus externe (+ dans ce cas) et utilisez la méthode Matches() au lieu de Match(). Chaque langage regex permet de faire cela: en Perl ou en JavaScript, vous faites la correspondance en mode /g; dans Ruby, vous utilisez la méthode scan; en Java, vous appelez find() à plusieurs reprises jusqu'à ce qu'il renvoie false. De même, si vous effectuez une opération de remplacement, vous pouvez rebrancher les sous-chaînes capturées au fur et à mesure avec des espaces réservés ($1, $2 ou \1, \2, selon la langue). D'autre part, je ne connais pas d'autre saveur regex dérivée de Perl 5 qui offre la possibilité de récupérer des correspondances de groupes de capture intermédiaires comme .NET le fait avec ses CaptureCollections. Et je ne suis pas surpris: il est très rare que vous ayez vraiment besoin de capturer tous les matchs en une fois comme ça. Et pensez à toute la puissance de stockage et/ou de traitement nécessaire pour suivre toutes ces correspondances intermédiaires. C'est une belle fonctionnalité cependant.

Questions connexes