Je suis un peu flou sur la différence entre un "groupe" et une "capture" en ce qui concerne le langage d'expression régulière de .NET. Considérez le code C # suivant:
MatchCollection matches = Regex.Matches("{Q}", @"^\{([A-Z])\}$");
Je m'attends à ce que cela se traduise par une capture unique pour la lettre "Q", mais si j'imprime les propriétés du MatchCollection
retourné, je vois:
matches.Count: 1
matches[0].Value: {Q}
matches[0].Captures.Count: 1
matches[0].Captures[0].Value: {Q}
matches[0].Groups.Count: 2
matches[0].Groups[0].Value: {Q}
matches[0].Groups[0].Captures.Count: 1
matches[0].Groups[0].Captures[0].Value: {Q}
matches[0].Groups[1].Value: Q
matches[0].Groups[1].Captures.Count: 1
matches[0].Groups[1].Captures[0].Value: Q
Que se passe-t-il exactement ici? Je comprends qu'il y a aussi une capture pour tout le match, mais comment les groupes entrent-ils? Et pourquoi matches[0].Captures
inclure la capture de la lettre 'Q'?
Vous ne serez pas le premier à être flou à ce sujet. Voici ce qu'en dit le célèbre Jeffrey Friedl (pages 437+):
Selon votre point de vue, cela ajoute une nouvelle dimension intéressante aux résultats du match, ou ajoute de la confusion et des ballonnements.
Et plus loin:
La principale différence entre un objet Group et un objet Capture est que chaque objet Group contient une collection de Captures représentant toutes les correspondances intermédiaires par le groupe pendant la correspondance , ainsi que le texte final correspondant au groupe.
Et quelques pages plus tard, voici sa conclusion:
Après avoir dépassé la documentation .NET et réellement compris ce que ces objets ajoutent, j'ai des sentiments mitigés à leur sujet. D'une part, c'est une innovation intéressante [..] d'autre part, il semble ajouter un fardeau d'efficacité [..] d'une fonctionnalité qui ne sera pas utilisée dans la majorité des cas
En d'autres termes: ils sont très similaires, mais de temps en temps et en l'occurrence, vous en trouverez une utilisation. Avant de faire pousser une autre barbe grise, vous pouvez même aimer les Captures ...
Étant donné que ni ce qui précède, ni ce qui est dit dans l'autre post ne semble vraiment répondre à votre question, considérez ce qui suit. Considérez les captures comme une sorte de traqueur d'histoire. Lorsque l'expression régulière fait sa correspondance, elle parcourt la chaîne de gauche à droite (en ignorant le retour arrière pendant un moment) et lorsqu'elle rencontre une parenthèse de capture correspondante, elle la stockera dans $x
(x étant n'importe quel chiffre), disons $1
.
Les moteurs d'expression régulière normaux, lorsque les parenthèses de capture doivent être répétées, jetteront le courant $1
et le remplacera par la nouvelle valeur. Pas .NET, qui conservera cet historique et le placera dans Captures[0]
.
Si nous changeons votre expression régulière pour qu'elle se présente comme suit:
MatchCollection matches = Regex.Matches("{Q}{R}{S}", @"(\{[A-Z]\})+");
vous remarquerez que le premier Group
aura un Captures
(le premier groupe étant toujours la correspondance entière, c'est-à-dire, égal à $0
) et le deuxième groupe tiendra {S}
, c'est-à-dire uniquement le dernier groupe correspondant. Cependant, et voici la capture, si vous voulez trouver les deux autres captures, elles sont dans Captures
, qui contient toutes les captures intermédiaires pour {Q}
{R}
et {S}
.
Si vous vous êtes déjà demandé comment obtenir la capture multiple, qui n'affiche que la dernière correspondance avec les captures individuelles clairement présentes dans la chaîne, vous devez utiliser Captures
.
Un dernier mot sur votre dernière question: la correspondance totale a toujours une capture totale, ne mélangez pas cela avec les groupes individuels. Les captures ne sont intéressantes qu'à l'intérieur des groupes .
Un groupe est ce que nous avons associé à des groupes dans les expressions régulières
"(a[zx](b?))"
Applied to "axb" returns an array of 3 groups:
group 0: axb, the entire match.
group 1: axb, the first group matched.
group 2: b, the second group matched.
sauf que ce ne sont que des groupes "capturés". Les groupes non capturants (utilisant la syntaxe '(?:') Ne sont pas représentés ici.
"(a[zx](?:b?))"
Applied to "axb" returns an array of 2 groups:
group 0: axb, the entire match.
group 1: axb, the first group matched.
Une capture est également ce que nous avons associé aux "groupes capturés". Mais lorsque le groupe est appliqué plusieurs fois avec un quantificateur, seule la dernière correspondance est conservée comme correspondance du groupe. Le tableau de captures stocke toutes ces correspondances.
"(a[zx]\s+)+"
Applied to "ax az ax" returns an array of 2 captures of the second group.
group 1, capture 0 "ax "
group 1, capture 1 "az "
Quant à votre dernière question - j'aurais pensé avant d'examiner cela que Captures serait un tableau des captures ordonnées par le groupe auquel elles appartiennent. Il s'agit plutôt d'un alias pour les groupes [0] .Captures. Assez inutile ..
Depuis le MSDN documentation :
L'utilité réelle de la propriété Captures se produit lorsqu'un quantificateur est appliqué à un groupe de capture afin que le groupe capture plusieurs sous-chaînes dans une seule expression régulière. Dans ce cas, l'objet Group contient des informations sur la dernière sous-chaîne capturée, tandis que la propriété Captures contient des informations sur toutes les sous-chaînes capturées par le groupe. Dans l'exemple suivant, l'expression régulière\b (\ w +\s *) +. correspond à une phrase entière qui se termine par un point. Le groupe (\ w +\s *) + capture les mots individuels de la collection. Étant donné que la collection Group contient uniquement des informations sur la dernière sous-chaîne capturée, elle capture le dernier mot de la phrase, "phrase". Cependant, chaque mot capturé par le groupe est disponible à partir de la collection retournée par la propriété Captures.
Cela peut être expliqué par un exemple simple (et des images).
Correspondance 3:10pm
avec l'expression régulière ((\d)+):((\d)+)(am|pm)
, et en utilisant Mono interactive csharp
:
csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)").
> Groups.Cast<Group>().
> Zip(Enumerable.Range(0, int.MaxValue), (g, n) => "[" + n + "] " + g);
{ "[0] 3:10pm", "[1] 3", "[2] 3", "[3] 10", "[4] 0", "[5] pm" }
Puisqu'il y a plusieurs chiffres qui correspondent sur le quatrième groupe, nous n'obtenons la dernière correspondance que si nous référençons le groupe (avec une ToString()
implicite, c'est-à-dire). Afin d'exposer les correspondances intermédiaires, nous devons aller plus loin et référencer la propriété Captures
sur le groupe en question:
csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)").
> Groups.Cast<Group>().
> Skip(4).First().Captures.Cast<Capture>().
> Zip(Enumerable.Range(0, int.MaxValue), (c, n) => "["+n+"] " + c);
{ "[0] 1", "[1] 0" }
Gracieuseté de cet article .
Imaginez que vous ayez la saisie de texte suivante dogcatcatcat
et un modèle comme dog(cat(catcat))
Dans ce cas, vous avez 3 groupes, le premier ( grand groupe ) correspond à la correspondance.
Faire correspondre == dogcatcatcat
et Group0 == dogcatcatcat
Groupe1 == catcatcat
Groupe2 == catcat
Alors de quoi s'agit-il?
Prenons un petit exemple écrit en C # (.NET) en utilisant la classe Regex
.
int matchIndex = 0;
int groupIndex = 0;
int captureIndex = 0;
foreach (Match match in Regex.Matches(
"dogcatabcdefghidogcatkjlmnopqr", // input
@"(dog(cat(...)(...)(...)))") // pattern
)
{
Console.Out.WriteLine($"match{matchIndex++} = {match}");
foreach (Group @group in match.Groups)
{
Console.Out.WriteLine($"\tgroup{groupIndex++} = {@group}");
foreach (Capture capture in @group.Captures)
{
Console.Out.WriteLine($"\t\tcapture{captureIndex++} = {capture}");
}
captureIndex = 0;
}
groupIndex = 0;
Console.Out.WriteLine();
}
Sortie:
match0 = dogcatabcdefghi
group0 = dogcatabcdefghi
capture0 = dogcatabcdefghi
group1 = dogcatabcdefghi
capture0 = dogcatabcdefghi
group2 = catabcdefghi
capture0 = catabcdefghi
group3 = abc
capture0 = abc
group4 = def
capture0 = def
group5 = ghi
capture0 = ghi
match1 = dogcatkjlmnopqr
group0 = dogcatkjlmnopqr
capture0 = dogcatkjlmnopqr
group1 = dogcatkjlmnopqr
capture0 = dogcatkjlmnopqr
group2 = catkjlmnopqr
capture0 = catkjlmnopqr
group3 = kjl
capture0 = kjl
group4 = mno
capture0 = mno
group5 = pqr
capture0 = pqr
Analysons juste la première correspondance (match0
).
Comme vous pouvez le voir, il existe trois groupes mineurs : group3
, group4
et group5
group3 = kjl
capture0 = kjl
group4 = mno
capture0 = mno
group5 = pqr
capture0 = pqr
Ces groupes (3-5) ont été créés en raison du sous-modèle '' (...)(...)(...)
du motif principal (dog(cat(...)(...)(...)))
Valeur de group3
correspond à sa capture (capture0
). (Comme dans le cas de group4
et group5
). C'est parce que il n'y a pas de répétition de groupe comme (...){3}
.
Ok, considérons un autre exemple où il y a répétition de groupe .
Si nous modifions le modèle d'expression régulière à mettre en correspondance (pour le code ci-dessus) à partir de (dog(cat(...)(...)(...)))
à (dog(cat(...){3}))
, vous remarquerez qu'il y a ce qui suit répétition de groupe: (...){3}
.
Maintenant, le Sortie a changé:
match0 = dogcatabcdefghi
group0 = dogcatabcdefghi
capture0 = dogcatabcdefghi
group1 = dogcatabcdefghi
capture0 = dogcatabcdefghi
group2 = catabcdefghi
capture0 = catabcdefghi
group3 = ghi
capture0 = abc
capture1 = def
capture2 = ghi
match1 = dogcatkjlmnopqr
group0 = dogcatkjlmnopqr
capture0 = dogcatkjlmnopqr
group1 = dogcatkjlmnopqr
capture0 = dogcatkjlmnopqr
group2 = catkjlmnopqr
capture0 = catkjlmnopqr
group3 = pqr
capture0 = kjl
capture1 = mno
capture2 = pqr
Encore une fois, analysons uniquement la première correspondance (match0
).
Il n'y a plus de groupes mineurs group4
et group5
à cause de (...){3}
répétition ( {n} où n> = 2 ) ils ont été fusionnés en un seul groupe group3
.
Dans ce cas, le group3
la valeur correspond à son capture2
( la dernière capture , en d'autres termes).
Ainsi, si vous avez besoin des 3 captures internes (capture0
, capture1
, capture2
), vous devrez parcourir la collection Captures
du groupe.
La conclusion est: faites attention à la façon dont vous concevez les groupes de votre modèle. Vous devriez penser à l'avance quel comportement provoque la spécification du groupe, comme (...)(...)
, (...){2}
ou (.{3}){2}
etc.
J'espère que cela aidera à faire la lumière sur les différences entre Captures, Groupes et Correspondances également.