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

y a-t-il un comportement défini pour la façon dont les expressions régulières devraient gérer le comportement de capture des parenthèses imbriquées? Plus précisément, pouvez-vous raisonnablement vous attendre à ce que différents moteurs saisissent les parenthèses extérieures à la première position, et les parenthèses imbriquées dans les positions suivantes?

Considérer 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
)

toute l'expression entre parenthèses est capturée en premier (I souhaitez tester), et puis l'interne entre parenthèses modèles sont capturés suivant ("vouloir" et "pour"). Cela semble logique, mais je pourrais voir un cas tout aussi logique étant fait pour capturer d'abord les sous-parenthèses, et puis capturer le modèle entier.

donc, est-ce que ce "capturer la chose entière d'abord" comportement défini dans les moteurs à expression régulière, ou va-t-il dépendre du contexte du modèle et/ou le comportement du moteur (PCRE étant différent de C's étant différent de Java étant différent de etc.)?

59
demandé sur Alan Storm 2009-08-21 23:54:53

4 réponses

de perlrequick

si les groupements dans une regex sont imbriqués, $1 obtient le groupe avec l' ouverture à gauche de la parenthèse, $2 le ouverture suivante entre parenthèses, etc.

Caveat : à L'exclusion des parenthèses d'ouverture du groupe non capturé (?= )

mise à Jour

Je n'utilise pas beaucoup PCRE, car je généralement utiliser la chose réelle;), mais PCRE's docs montrer la même chose que Perl:

SUBPATTERNS

2. il définit le subpattern comme un subpattern de capture. Cela signifie que, lorsque le motif complet correspond, la partie de la chaîne de caractères qui correspond au subpattern est transmise à l'appelant via l'argument ovector de pcre_exec() . L'ouverture de parenthèses sont comptés de gauche à droite (à partir de 1) pour obtenir le nombre pour les souspatternes de capture.

par exemple, si la chaîne" The red king "est comparée au motif

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 s'éloigne de la compatibilité regex Perl, peut-être que l'acronyme devrait être redéfini -- " Perl Apparenté Expressions Régulières", "Perl Comparable Expressions Régulières" ou quelque chose. Ou juste se départir des lettres de signification.

46
répondu daotoad 2018-05-24 07:32:41

Oui, tout cela est assez bien défini pour toutes les langues qui vous intéressent:

  • Java - http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg

    "Les groupes de capture sont numérotés en comptant leurs parenthèses d'ouverture de gauche à droite. ... Le groupe zéro représente toujours toute l'expression."
  • .Net - http://msdn.microsoft.com/en-us/library/bs2twtah (VS.71).aspx

    "Les Captures utilisant () sont numérotées automatiquement selon l'ordre de la parenthèse d'ouverture, à partir d'une. 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 functions) - 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. Les parenthèses d'ouverture sont comptées de gauche à droite (à partir de 1) pour obtenir le numéro du sous-marin capturé."(Cela était également vrai des fonctions POSIX dépréciées)
  • PCRE - http://www.pcre.org/pcre.txt

    Pour ajouter à ce Qu'a dit Alan M, cherchez "How pcre_exec () returns captured substrings" et lisez le cinquième paragraphe qui suit:

    The  first  pair  of  integers, ovector[0] and ovector[1], identify the
    portion of the subject string matched by the entire pattern.  The next
    pair  is  used for the first capturing subpattern, and so on. The value
    returned by pcre_exec() is one more than the highest numbered pair that
    has  been  set.  For example, if two substrings have been captured, the
    returned value is 3. If there are no capturing subpatterns, the  return
    value from a successful match is 1, indicating that just the first pair
    of offsets has been set.
    
  • Perl's different - http://perldoc.perl.org/perlre.html#Capture-buffers

    $1, $ 2 etc. faites correspondre les groupes de capture comme vous vous y attendiez (c.-à-d. par la fréquence d'ouverture de la fourchette), cependant 0 $renvoie le nom du Programme, pas le chaîne de requête entière - pour obtenir que vous utilisez $ & à la place.

vous trouverez probablement des résultats similaires pour D'autres langues (Python, Ruby, et autres).

vous dites qu'il est tout aussi logique d'énumérer les groupes de capture internes en premier et vous avez raison - il s'agit simplement d'indexer sur la fermeture, plutôt que d'ouvrir, parens. (si je vous comprends bien). Faire cela est moins naturel cependant (par exemple, il ne suit pas la direction de lecture il est donc plus difficile (probablement pas de façon significative) de déterminer, par inseption, quel groupe de capture sera à un indice de résultat donné.

mettre toute la chaîne de match étant en position 0 a aussi du sens - surtout pour la cohérence. Il permet à la chaîne appariée entière de rester dans le même index quel que soit le nombre de groupes de capture de regex à regex et quel que soit le nombre de groupes de capture qui correspondent à quelque chose (Java pour exemple va s'effondrer la longueur du tableau de groupes appariés pour chaque groupe de capture ne correspond à aucun contenu (pensez par exemple quelque chose comme "a (.*)modèle.)" Vous pouvez toujours inspecter capturing_group_results[capturing_group_results_length-2], mais cela ne traduit pas bien aux langues Perl qui créent dynamiquement des variables ($1, $2 etc.) (Perl est un mauvais exemple bien sûr, puisqu'il utilise $ & pour l'expression associée, mais vous obtenez l'idée:).

14
répondu Alan Donnelly 2013-08-03 17:56:00

chaque saveur regex je sais groupes de nombres par l'ordre dans lequel les parenthèses d'ouverture apparaissent. Le fait que les groupes externes soient numérotés avant leurs sous-groupes n'est qu'un résultat naturel, et non une politique explicite.

où il devient intéressant est avec groupes nommés . Dans la plupart des cas, ils suivent la même Politique de numérotation par les positions relatives des parens--le nom est simplement un alias pour le numéro. Toutefois, in. net regexe le nommé groupes sont numérotés séparément de numérotée groupes. Par exemple:

Regex.Replace(@"one two three four", 
              @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)",
              @"   ")

// result: "two four one three"

en effet, le numéro est un alias pour le nom ; les numéros attribués aux groupes nommés commencent là où les" vrais " groupes numérotés s'arrêtent. Cela peut sembler étrange, mais il y a une bonne raison à cela: dans les regexes.Net vous pouvez utiliser le même nom de groupe plus d'une fois dans un regex. Cela rend possible regexes comme celui de ce fil pour la correspondance des nombres à virgule flottante à partir de différents paramètres régionaux:

^[+-]?[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})?
)$

S'il y a un séparateur de milliers, il sera sauvegardé dans le groupe "thousand" peu importe quelle partie du regex correspond. De même, le séparateur décimal (s'il y en a un) sera toujours sauvegardé dans le groupe "décimal". Bien sûr, il y a des façons d'identifier et d'extraire les séparateurs sans groupes nommés réutilisables, mais cette façon est tellement plus pratique, je pense cela justifie amplement l'étrange système de numérotation.

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

8
répondu Alan Moore 2017-05-23 11:54:51

L'ordre de la capture dans l'ordre de gauche parenthèse est standard sur toutes les plateformes que j'ai travaillé. (perl, php, ruby, egrep)

4
répondu Devin Ceartas 2013-04-10 02:30:28