web-dev-qa-db-fra.com

Faire correspondre plusieurs motifs d'expression régulière avec l'opérateur d'alternance?

J'ai rencontré un petit problème en utilisant Python Regex.

Supposons que ce soit l'entrée:

(zyx)bc

Ce que j'essaie de réaliser, c'est d'obtenir ce qui se trouve entre les parenthèses en tant que correspondance unique et tout caractère extérieur en tant que correspondance individuelle. Le résultat souhaité serait le suivant:

['zyx','b','c']

L'ordre des matchs doit être conservé.

J'ai essayé d'obtenir cela avec Python 3.3, mais je n'arrive pas à comprendre le bon regex. Jusqu'à présent, j'ai:

matches = findall(r'\((.*?)\)|\w', '(zyx)bc')

print(matches) donne les résultats suivants:

['zyx','','']

Des idées sur ce que je fais mal?

12
Julian Laval

D'après la documentation de re.findall:

Si un ou plusieurs groupes sont présents dans le modèle, renvoyez une liste de groupes; ce sera une liste de tuples si le motif a plus d'un groupe.

Pendant que votre expression rationnelle correspond trois fois à la chaîne, le (.*?) le groupe est vide pour les deux seconds matchs. Si vous voulez la sortie de l'autre moitié de l'expression rationnelle, vous pouvez ajouter un deuxième groupe:

>>> re.findall(r'\((.*?)\)|(\w)', '(zyx)bc')
[('zyx', ''), ('', 'b'), ('', 'c')]

Alternativement, vous pouvez supprimer tous les groupes pour obtenir à nouveau une simple liste de chaînes:

>>> re.findall(r'\(.*?\)|\w', '(zyx)bc')
['(zyx)', 'b', 'c']

Vous devrez cependant supprimer manuellement les parenthèses.

12
James Henstridge

Jetons un œil à notre sortie en utilisant re.DEBUG.

branch 
  literal 40 
  subpattern 1 
    min_repeat 0 65535 
      any None 
  literal 41 
or
  in 
    category category_Word

Aïe, il n'y a qu'un subpattern mais re.findall ne sort que subpatterns s'il en existe un!

a = re.findall(r'\((.*?)\)|(.)', '(zyx)bc',re.DEBUG); a
[('zyx', ''), ('', 'b'), ('', 'c')]
branch 
  literal 40 
  subpattern 1 
    min_repeat 0 65535 
      any None 
  literal 41 
or
  subpattern 2 
    any None

Mieux. :)

Il ne nous reste plus qu'à en faire le format que vous souhaitez.

[i[0] if i[0] != '' else i[1] for i in a]
['zyx', 'b', 'c']
3
Fredrick Brennan

Les documents mentionnent spécialement le traitement des groupes, donc ne mettez pas de groupe autour du modèle entre parenthèses, et vous obtiendrez tout, mais vous devrez supprimer vous-même les parens des données correspondantes:

>>> re.findall(r'\(.+?\)|\w', '(zyx)bc')
['(zyx)', 'b', 'c']

ou utilisez plus de groupes, puis traitez les tuples résultants pour obtenir les chaînes que vous recherchez:

>>> [''.join(t) for t in re.findall(r'\((.+?)\)|(\w)', '(zyx)bc')]
>>> ['zyx', 'b', 'c']
1
Ned Batchelder
In [108]: strs="(zyx)bc"

In [109]: re.findall(r"\(\w+\)|\w",strs)
Out[109]: ['(zyx)', 'b', 'c']

In [110]: [x.strip("()") for x in re.findall(r"\(\w+\)|\w",strs)]
Out[110]: ['zyx', 'b', 'c']
1
Ashwini Chaudhary

D'autres réponses vous ont montré comment obtenir le résultat dont vous avez besoin, mais avec l'étape supplémentaire de suppression manuelle des parenthèses. Si vous utilisez des contournements dans votre expression régulière, vous n'aurez pas besoin de supprimer les parenthèses manuellement:

>>> import re
>>> s = '(zyx)bc'
>>> print (re.findall(r'(?<=\()\w+(?=\))|\w', s))
['zyx', 'b', 'c']

Expliqué:

(?<=\() // lookbehind for left parenthesis
\w+     // all characters until:
(?=\))  // lookahead for right parenthesis
|       // OR
\w      // any character
1
alan