95 votes

Comment les groupes de capture imbriqués sont-ils numérotés dans les expressions régulières?

Est-il un comportement défini de la façon dont les expressions régulières doivent gérer la capture de comportement de parenthèses imbriquées? Plus précisément, pouvez-vous raisonnablement s'attendre à ce que les différents moteurs de la capture à l'extérieur des parenthèses dans la première position, et les parenthèses imbriquées dans la suite des positions?

Considérons le code PHP suivant (en utilisant les expressions régulières PCRE)

<?php
  $test_string = 'I want to test sub patterns';
  preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches);
  print_r($matches);
?>

Array
(
	[0] => I want to test sub patterns	//entire pattern
	[1] => I want to test			//entire outer parenthesis
	[2] => want				//first inner
	[3] => to				//second inner
	[4] => patterns				//next parentheses set
)

L'ensemble de la mise entre parenthèses de l'expression est capturé en premier (je veux tester), et puis l'interne entre parenthèses modèles sont capturés suivant ("vouloir" et "pour"). C'est tout à fait logique, mais je pouvais voir tout aussi logique cas être pour la première capture de la sous parenthèses, PUIS en capturant l'ensemble du motif.

Alors, est-ce "la capture de la totalité de la chose" comportement défini dans des moteurs d'expressions régulières, ou est-ce que ça va dépendre du contexte de la structure et/ou le comportement du moteur (PCRE être différente de C#étant différent de Java est différent, etc.)?

64voto

daotoad Points 17916

De perlrequick

Si les regroupements dans les regex sont imbriqués, $1 obtient le groupe avec l' plus à gauche de l'ouverture de la parenthèse, $2 de la prochaine ouverture de la parenthèse, etc.

Mise à jour

Je n'utilise pas PCRE beaucoup, car je l'utilise généralement la vraie chose ;), mais PCRE de docs montrer le même que Perl:

Les sous-masques

2. Il met en place le sous-masque, comme la capture d'un sous-modèle. Cela signifie que, lorsque l'ensemble du motif correspond, la portion de la sous-chaîne qui correspond au sous-masque est transmis à l'appelant par l' ovector argument de l' pcre_exec(). L'ouverture de parenthèses sont comptés à partir de la gauche vers la droite (à partir de 1) pour obtenir le nombre de capture de sous-masques.

Par exemple, si la chaîne "le roi soleil" qui est en correspondance avec le modèle

the ((red|white) (king|queen))

les chaînes capturées sont "roi", "rouge", et "le roi", et sont numérotées 1, 2, et 3, respectivement.

Si PCRE est à la dérive loin de l'expression rationnelle Perl compatibilité, peut-être l'acronyme doit être redéfini--"Perl Apparenté Expressions Régulières", "Perl Comparable Expressions Régulières" ou quelque chose. Ou tout simplement se dessaisir des lettres de sens.

19voto

Alan Donnelly Points 1133

Ouais, c'est tous assez bien défini pour toutes les langues qui vous intéresse:

  • Java - http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
    "Capturer les groupes sont numérotés en fonction de comptage d'ouverture de parenthèses, de gauche à droite. ... Groupe zéro est toujours pour l'ensemble de l'expression."
  • .Net - http://msdn.microsoft.com/en-us/library/bs2twtah(SV.71).aspx
    "Capture à l'aide de () sont numérotées automatiquement, basée sur le bon de commande de l'ouverture de la parenthèse, à partir de l'un. La première capture, capture le nombre d'élément zéro, est le texte correspondant à l'ensemble du modèle d'expression régulière.")
  • PHP (PCRE fonctions) - http://www.php.net/manual/en/function.preg-replace.php#function.preg-replace.parameters
    "\0 ou 0 $désigne le texte correspondant à l'ensemble du motif. L'ouverture de parenthèses sont comptés à partir de la gauche vers la droite (à partir de 1) pour obtenir le numéro de la capture d'un sous-modèle." (Il est également vrai de l'obsolète fonctions POSIX)
  • PCRE - http://www.pcre.org/pcre.txt
    Pour ajouter à ce qu'Alan M a dit, de la recherche pour "Comment pcre_exec() renvoie chaînes capturées" et de lire le cinquième paragraphe qui suit:

    La première paire d'entiers, ovector[0] et ovector[1], d'identifier les
    portion de la sous-chaîne correspondant à la totalité de modèle. La prochaine
    paire est utilisé pour la première capture d'un sous-modèle, et ainsi de suite. La valeur
    renvoyé par pcre_exec() est un de plus que le plus haut numéro de la paire qui
    a été défini. Par exemple, si deux sous-chaînes ont été capturés, le
    la valeur de retour est de 3. Si il n'y a pas de capture de sous-masques, le retour
    la valeur d'un match de succès est de 1, indiquant que la première paire
    des opérations de compensation a été fixé.
    
  • Perl est différent - http://perldoc.perl.org/perlre.html#Capture-buffers
    $1, $2, etc. match de capturer les groupes comme vous le souhaitez (c'est à dire par l'apparition de l'ouverture de la parenthèse), mais $0 retourne le nom du programme, et non pas l'ensemble de la chaîne de requête - pour obtenir que vous utilisez $& à la place.

Vous aurez plus de chances de trouver des résultats similaires pour d'autres langages (Python, Ruby, et autres).

Vous dites qu'il est tout aussi logique de la liste intérieure groupes de capture en premier et vous avez raison - c'est juste une question de l'indexation sur la clôture, plutôt que d'ouvrir des parenthèses. (si je vous comprends bien). Cela est moins naturelle si, par exemple, il ne suit pas les sens de lecture de la convention) et fait en sorte qu'il est plus difficile (sans doute pas de façon significative) pour déterminer, par insepection, qui capture d'un groupe sera à un résultat donné index.

Mettre le match en entier chaîne est à la position 0 est également logique - principalement pour des raisons de cohérence. Il permet à l'ensemble de la chaîne correspond à rester dans le même index quel que soit le nombre de capturer les groupes de regex pour regex et quel que soit le nombre de capture de groupes qui correspondent réellement quoi que ce soit (Java par exemple un effondrement de la longueur de la correspondance des groupes de tableau pour chaque capture d'un groupe ne correspond pas à n'importe quel contenu (pense, par exemple, quelque chose comme "un (.*)pattern"). Vous pouvez toujours inspecter capturing_group_results[capturing_group_results_length - 2], mais qui ne se traduit pas bien les langues de Perl qui créer dynamiquement des variables ($1, $2, etc.) (Perl est un mauvais exemple, bien sûr, car il utilise $& pour les paires de l'expression, mais vous voyez l'idée :).

11voto

Alan Moore Points 39365

Tous les regex saveur je sais que nombre de groupes par l'ordre dans lequel l'ouverture de parenthèses apparaissent. Que les groupes externes sont numérotés en avant leurs contenus sous-groupes est juste un résultat naturel, pas de politique explicite.

Là où ça devient intéressant, c'est avec des groupes nommés. Dans la plupart des cas, ils suivent la même politique de numérotation par les positions relatives de la parens--le nom est simplement un alias pour le nombre. Toutefois, dans .NET regexes les groupes nommés sont numérotées séparément de numérotée groupes. Par exemple:

Regex.Replace(@"one two three four", 
              @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)",
              @"$1 $2 $3 $4")

// result: "two four one three"

En effet, le nombre est un alias pour le nom; les numéros attribués à des groupes nommés commencer là où le "réel" numérotées groupes de laisser tomber. Cela peut sembler bizarre de la politique, mais il y a une bonne raison pour cela: dans .NET regexes vous pouvez utiliser le même nom de groupe de plus d'une fois dans une expression régulière. Qui rend possible regexes comme celui de ce fil de discussion pour la mise en correspondance des nombres à virgule flottante à partir de lieux différents:

^[+-]?[0-9]{1,3}
(?:
    (?:(?<thousand>\,)[0-9]{3})*
    (?:(?<decimal>\.)[0-9]{2})?
|
    (?:(?<thousand>\.)[0-9]{3})*
    (?:(?<decimal>\,)[0-9]{2})?
|
    [0-9]*
    (?:(?<decimal>[\.\,])[0-9]{2})?
)$

Si il y a un séparateur de milliers, il sera enregistré dans le groupe "mille" n'importe quelle partie de l'expression régulière adaptée. De même, le séparateur décimal (si il y en a un) sera toujours enregistrée dans le groupe "décimal". Bien sûr, il existe des moyens pour identifier et extraire les séparateurs sans réutilisables groupes nommés, mais ce moyen est bien plus pratique, je pense qu'il est plus que justifie l'étrange système de numérotation.

Et puis, il y a de Perl 5.10+, ce qui nous donne plus de contrôle sur la capture des groupes que je sais quoi faire avec. :D

4voto

Devin Ceartas Points 3719

L'ordre de capture dans l'ordre du parent gauche est standard sur toutes les plateformes sur lesquelles j'ai travaillé. (Perl, php, ruby, egrep)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X