re.findall ('(ab | cd)', chaîne) vs re.findall ('(ab | cd) +', chaîne)

18

Dans une expression régulière Python, je rencontre ce problème singulier. Pourriez-vous donner des instructions sur les différences entre re.findall('(ab|cd)', string)et re.findall('(ab|cd)+', string)?

import re

string = 'abcdla'
result = re.findall('(ab|cd)', string)
result2 = re.findall('(ab|cd)+', string)
print(result)
print(result2)

La sortie réelle est:

['ab', 'cd']
['cd']

Je ne sais pas pourquoi le deuxième résultat ne contient pas 'ab'aussi bien?

Roche
la source
re.findall ('(ab | cd)', chaîne) obtient ['ab', 'cd'] re.findall ('(ab | cd) +', chaîne) obtient ['cd']
rock le

Réponses:

15

+est un quantificateur de répétition qui correspond une ou plusieurs fois. Dans l'expression régulière (ab|cd)+, vous répétez le groupe de capture à l' (ab|cd) aide de +. Cela ne capturera que la dernière itération.

Vous pouvez raisonner à propos de ce comportement comme suit:

Dites que votre chaîne est abcdlaet regex l'est (ab|cd)+. Le moteur Regex trouvera une correspondance pour le groupe entre les positions 0 et 1 au fur abet à mesure qu'il quitte le groupe de capture. Ensuite, il voit le +quantificateur et essaie donc de capturer à nouveau le groupe et capturera cdentre les positions 2 et 3.


Si vous souhaitez capturer toutes les itérations, vous devez plutôt capturer le groupe extensible avec ((ab|cd)+)lequel correspond abcdet cd. Vous pouvez faire en sorte que le groupe interne ne soit pas capturé car nous ne nous soucions pas des correspondances de groupe interne avec ((?:ab|cd)+)quelles correspondancesabcd

https://www.regular-expressions.info/captureall.html

Depuis les documents,

Supposons que vous souhaitez faire correspondre une balise comme !abc!ou !123!. Seuls ces deux sont possibles, et vous voulez capturer le abcou 123pour savoir quelle balise vous avez obtenue. C'est assez simple: !(abc|123)!fera l'affaire.

Supposons maintenant que la balise puisse contenir plusieurs séquences de abcet 123, comme !abc123!ou !123abcabc!. La solution rapide et facile est !(abc|123)+!. Cette expression régulière correspondra en effet à ces balises. Cependant, il ne répond plus à notre exigence de capturer l'étiquette du tag dans le groupe de capture. Lorsque cette expression régulière correspond !abc123!, le groupe de capture stocke uniquement 123. Quand il correspond !123abcabc!, il ne fait que stocker abc.

Shashank V
la source
pouvez-vous créer un lien vers un document expliquant clairement que + ne capture que la dernière itération, et qu'est-ce qu'un groupe de capture?
Gulzar
1
@Gulzar, a mis à jour la réponse. Vous pouvez lire sur les groupes de capture ici - regular-expressions.info/refcapture.html
Shashank V
@Shashank, merci, votre réponse est exactement ce dont j'ai besoin. merci sincèrement
rock
@rock Veuillez accepter la réponse si elle a résolu votre question.
Shashank V
Il n'est pas nécessaire d'entourer toute l'expression régulière avec des crochets. Ça '(?:ab|cd)+'va marcher.
Dukeling
5

Je ne sais pas si cela clarifiera davantage les choses, mais essayons d'imaginer ce qui se passe sous le capot d'une manière simple, nous allons résumer ce qui se passe en utilisant match

   # group(0) return the matched string the captured groups are returned in groups or you can access them
   # using group(1), group(2).......  in your case there is only one group, one group will capture only 
   # one part so when you do this
   string = 'abcdla'
   print(re.match('(ab|cd)', string).group(0))  # only 'ab' is matched and the group will capture 'ab'
   print(re.match('(ab|cd)+', string).group(0)) # this will match 'abcd'  the group will capture only this part 'cd' the last iteration

findallcorrespondre et consommer la chaîne en même temps imaginons ce qui se passe avec ce REGEX '(ab|cd)':

      'abcdabla' ---> 1:   match: 'ab' |  capture : ab  | left to process:  'cdabla'
      'cdabla'   ---> 2:   match: 'cd' |  capture : cd  | left to process:  'abla'
      'abla'     ---> 3:   match: 'ab' |  capture : ab  | left to process:  'la'
      'la'       ---> 4:   match: '' |  capture : None  | left to process:  ''

      --- final : result captured ['ab', 'cd', 'ab']  

Maintenant, la même chose avec '(ab|cd)+'

      'abcdabla' ---> 1:   match: 'abcdab' |  capture : 'ab'  | left to process:  'la'
      'la'       ---> 2:   match: '' |  capture : None  | left to process:  ''
      ---> final result :   ['ab']  

J'espère que cela clarifie un peu les choses.

Charif DZ
la source
0

Donc, pour moi, une partie déroutante était le fait que

Si un ou plusieurs groupes sont présents dans le modèle, renvoyez une liste de groupes;

docs

il ne vous renvoie donc pas une correspondance complète, mais uniquement une correspondance de capture. Si vous faites en sorte que ce groupe ne capture pas (re.findall('(?:ab|cd)+', string), il reviendra ["abcd"]comme je m'y attendais initialement

RiaD
la source
Je ne sais pas trop ce que vous
attendiez