Confusion avec le groupement atomique - en quoi diffère-t-il du groupement en expression régulière de Ruby?

je viens de parcourir les documents pour groupe atomique et rubyinfo et quelques questions rapides sont venus à mon esprit comme suit:

  1. pourquoi le nom est venu comme "groupe atomique" ? Ce "atomicité" il a cette général de regroupement n'a pas.
  2. comment groupe atomique diffère-t-il du groupe général ?
  3. Pourquoi groupes atomiques sont appelés non-capture groupes?

j'ai essayé le code ci-dessous pour comprendre mais avait la confusion au sujet de la sortie et comment différemment de travailler sur la même chaîne?

irb(main):001:0> /a(?>bc|b)c/ =~ "abbcdabcc"
=> 5
irb(main):004:0> $~
=> #<MatchData "abcc">
irb(main):005:0> /a(bc|b)c/ =~ "abcdabcc"
=> 0
irb(main):006:0> $~
=> #<MatchData "abc" 1:"b">
16
demandé sur nhahtdh 2013-01-19 10:31:59

3 réponses

A () possède certaines propriétés (incluez celles telles que (?!pattern) , (?=pattern) , etc. et le simple (pattern) ), mais la propriété commune entre tous est grouping , ce qui rend le modèle arbitraire une unité (unité est ma propre terminologie), ce qui est utile dans la répétition.

normal capture (pattern) a la propriété de capture et groupe . Capturer signifie que le texte correspond au motif à l'intérieur sera capturé de sorte que vous pouvez l'utiliser avec back-reference, dans l'appariement ou le remplacement. Le groupe de non-capture (?:pattern) n'a pas la propriété de capture, donc il économisera un peu d'espace et accélérera un peu par rapport à (pattern) car il ne stocke pas l'index de début et de fin de la chaîne correspondant au motif à l'intérieur.

groupe atomique (?>pattern) a aussi la propriété de non-capture, donc la position le texte correspondant à l'intérieur ne seront pas capturées.

Atomique groupement ajoute la propriété de atomique par rapport à la capture ou de la non-capture d'un groupe. Atomic ici signifie: à la position actuelle, trouver la première séquence (la première est définie par la façon dont le moteur correspond selon le modèle donné) qui correspond au modèle à l'intérieur du groupement atomique et de s'y tenir (de sorte que le backtracking est interdit).

A groupe sans atomicité va permettre le backtracking - il va encore trouver la première séquence, puis si l'appariement en avant échoue, il va revenir en arrière et trouver la séquence suivante, jusqu'à ce qu'une correspondance pour l'expression de regex entière est trouvée ou toutes les possibilités sont épuisées.

exemple

chaîne de saisie: bbabbbabbbbc

Motif: /(?>.*)c/

Le premier match par .* est bbabbbabbbbc en raison de la gourmande quantificateur * . Il va s'accrocher à ce match, refusant c de correspondance. Le matcher va réessayer à la prochaine position jusqu'à la fin de la corde, et la même chose se produit. Donc rien ne correspond au regex.


chaîne de saisie: bbabbbabbbbc

Motif: /((?>.*)|b*)[ac]/ , pour l'essai /(((?>.*))|(b*))[ac]/

il y a 3 matchs regex, qui sont bba , bbba , bbbbc . Si vous utilisez le 2nd regex, qui est le même mais avec des groupes de capture ajoutés pour le but de débogage, vous pouvez voir que toutes les correspondances sont le résultat de la correspondance b* à l'intérieur.

vous pouvez voir le comportement de retracking ici.

  • sans le groupe atomique /(.*|b*)[ac]/ , la chaîne aura une correspondance simple qui est la chaîne entière, en raison de mandature en fin de match [ac] . Notez que le moteur va revenir à .* pour faire marche arrière de 1 caractère car il a encore d'autres possibilités.

    Pattern: /(.*|b*)[ac]/
    bbabbbabbbbc
    ^             -- Start matching. Look at first item in alternation: .*
    bbabbbabbbbc
                ^ -- First match of .*, due to greedy quantifier
    bbabbbabbbbc
                X -- [ac] cannot match
                  -- Backtrack to ()      
    bbabbbabbbbc
               ^  -- Continue explore other possibility with .*
                  -- Step back 1 character
    bbabbbabbbbc
                ^ -- [ac] matches, end of regex, a match is found
    
  • avec le groupe atomique, toutes les possibilités de .* sont coupées et limitées au premier match. Donc, après avide de manger toute la corde et ne pas correspondre, le moteur doit aller pour le modèle b* , où il trouve avec succès une correspondance pour les regex.

    Pattern: /((?>.*)|b*)[ac]/
    bbabbbabbbbc
    ^             -- Start matching. Look at first item in alternation: (?>.*)
    bbabbbabbbbc
                ^ -- First match of .*, due to greedy quantifier
                  -- The atomic grouping will disallow .* to be backtracked and rematched
    bbabbbabbbbc
                X -- [ac] cannot match
                  -- Backtrack to ()
                  -- (?>.*) is atomic, check the next possibility by alternation: b*
    bbabbbabbbbc
    ^             -- Starting to rematch with b*
    bbabbbabbbbc
      ^           -- First match with b*, due to greedy quantifier
    bbabbbabbbbc
       ^          -- [ac] matches, end of regex, a match is found
    

    les matchs suivants se poursuivront à partir d'ici.

38
répondu nhahtdh 2013-01-19 08:52:36

un" groupe atomique " est un groupe où l'expression régulière ne reviendra jamais en arrière. Donc dans votre premier exemple /a(?>bc|b)c/ si l'alternance bc dans le groupe correspond, alors il ne reculera jamais hors de cela et essayer l'alternance b . Si vous modifiez légèrement votre premier exemple pour qu'il corresponde à "abcdabcc" , vous verrez qu'il correspond toujours au "abcc" à la fin de la chaîne de caractères au lieu du "abc" au début. Si vous n'utilisez pas un groupe atomique, ensuite, il peut revenir en arrière après le bc et essayer l'alternance b et finir par correspondre au "abc" au début.

pour ce qui est de la deuxième question, en quoi elle est différente, c'est juste une reformulation de votre première question.

et enfin, les groupes atomiques ne sont pas" appelés " groupes non-capturants. Ce n'est pas un nom alternatif pour eux. Les groupes qui ne saisissent pas sont des groupes qui ne saisissent pas leur contenu. En général, quand vous correspondez à un régulier expression contre une chaîne, vous pouvez récupérer tous les groupes appariés, et si vous utilisez une substitution, vous pouvez utiliser des références arrières dans la substitution comme pour y insérer les groupes capturés. Mais un groupe non-capturant ne fournit pas cela. Le groupe classique de non-Capture est (?:pattern) . Il se trouve qu'un groupe atomique possède aussi la propriété non-capturing, d'où la raison pour laquelle on l'appelle un groupe non-capturing.

4
répondu Kevin Ballard 2013-01-19 07:34:18

j'ai récemment dû expliquer les groupes atomiques à quelqu'un d'autre et j'ai pensé que je pourrais modifier et partager l'exemple ici.

prendre en considération the (big|small|biggest) (cat|dog|bird) .

Correspond en gras

  • le grand chien
  • le petit oiseau
  • le plus grand chien
  • la petite cat

pour la première ligne, un moteur regex trouve the . Il procède ensuite à nos adjectifs ( big , small , biggest ), il trouve big . Ayant assorti "grand", il procède et trouve l'espace. Il regarde ensuite nos animaux de compagnie ( cat , dog , bird ) et trouve cat , le saute, et trouve dog .

pour la deuxième ligne, notre regex pourrait trouver the . Il faudrait procéder et regarder big , sauter, regarder et trouver small . Il trouve alors " ". Il ressemble au "chat", saute, regarde "chien", l'ignore, et trouve "l'oiseau".

pour la troisième ligne, notre regex trouverait the , Il continue sur et trouver big qui correspond au besoin immédiat , et produit. Il ne peut pas trouver l'espace, donc il backtracks (renvoie la position à la dernier choix qu'il fait). Il saute big , regarde small et le saute. Il trouve le plus grand qui correspond également au besoin immédiat . Il trouve alors " ". Il regarde cat et le saute, et correspond à dog .

pour la quatrième ligne, notre regex trouverait the . Il faudrait regarder big , sauter, regarder et trouver small . Il trouve alors " ". Il regarde et correspond à cat .

considérons Maintenant the (?>big|small|biggest) (cat|dog|bird) Notez le ?> atomique groupe sur les adjectifs.

Correspond en gras

  • le grand chien
  • le petit oiseau
  • le plus grand chien
  • le petit chat

Pour la première ligne, deuxième ligne, et à la quatrième ligne, notre moteur fonctionne de la même manière.

pour la troisième ligne, notre regex trouverait the , Il continue sur et trouver " grand "qui correspond à la exigence immédiate , et procède. Il ne peut pas trouver l'espace, mais le groupe atomique, étant le dernier choix que le moteur a fait, ne permettra pas que choix d'être réexaminé (interdit de retracer). Puisqu'il ne peut pas faire un nouveau choix, le match doit échouer, puisque notre simple expression n'a pas d'autre choix.

ceci n'est qu'un résumé. Un moteur n'aurait pas besoin de regarder la totalité de cat pour savoir qu'il ne correspond pas à dog , simplement en regardant le c est suffisant. En essayant de faire correspondre bird, le c dans cat et le d dans le chien sont assez pour dire au moteur d'examiner d'autres options.

cependant si vous aviez ... ((cat|snake)|dog|bird) , le moteur aurait aussi, bien sûr, besoin d'examiner le serpent avant qu'il ne tombe au groupe précédent et a examiné le chien et l'oiseau.

Il ya aussi beaucoup de choix un moteur ne peut pas décider sans passer ce qui peut sembler comme un match. Si vous avez ((red)?cat|dog|bird) , le moteur va regarder" r", sortir, noter le quantificateur ? , ignorer le sous-groupe (red) , et chercher une correspondance.

3
répondu Regular Joe 2017-03-02 03:58:28