Existe-t-il un moyen de déterminer le nombre de groupes de capture dans une expression régulière donnée?
J'aimerais pouvoir faire le suivi:
def groups(regexp, s):
""" Returns the first result of re.findall, or an empty default
>>> groups(r'(\d)(\d)(\d)', '123')
('1', '2', '3')
>>> groups(r'(\d)(\d)(\d)', 'abc')
('', '', '')
"""
import re
m = re.search(regexp, s)
if m:
return m.groups()
return ('',) * num_of_groups(regexp)
Cela me permet de faire des choses comme:
first, last, phone = groups(r'(\w+) (\w+) ([\d\-]+)', 'John Doe 555-3456')
Cependant, je ne sais pas comment implémenter num_of_groups
. (Actuellement, je travaille juste autour.)
EDIT: Suite aux conseils de rslite , j'ai remplacé re.findall
avec re.search
.
sre_parse
semble être la solution la plus robuste et la plus complète, mais nécessite une traversée d'arbre et semble un peu lourde.
L'expression régulière de MizardX semble couvrir toutes les bases, donc je vais y aller.
def num_groups(regex):
return re.compile(regex).groups
f_x = re.search(...)
len_groups = len(f_x.groups())
La propriété lastindex
de l'objet match doit être celle que vous recherchez. Voir les re module docs .
Quelque chose de l'intérieur de sre_parse pourrait aider.
À première vue, peut-être quelque chose du genre:
>>> import sre_parse
>>> sre_parse.parse('(\d)\d(\d)')
[('subpattern', (1, [('in', [('category', 'category_digit')])])),
('in', [('category', 'category_digit')]),
('subpattern', (2, [('in', [('category', 'category_digit')])]))]
C'est à dire. compter les éléments de type "sous-modèle":
import sre_parse
def count_patterns(regex):
"""
>>> count_patterns('foo: \d')
0
>>> count_patterns('foo: (\d)')
1
>>> count_patterns('foo: (\d(\s))')
1
"""
parsed = sre_parse.parse(regex)
return len([token for token in parsed if token[0] == 'subpattern'])
Notez que nous ne comptons que les modèles de niveau racine ici, donc le dernier exemple ne renvoie que 1. Pour changer cela, jetons devrait être recherché récursivement.
Tout d'abord, si vous n'avez besoin que du premier résultat de re.findall, il est préférable d'utiliser simplement re.search qui renvoie une correspondance ou None.
Pour le nombre de groupes, vous pouvez compter le nombre de parenthèses ouvertes '(' sauf celles qui sont échappées par '\'. Vous pouvez utiliser une autre expression régulière pour cela:
def num_of_groups(regexp):
rg = re.compile(r'(?<!\\)\(')
return len(rg.findall(regexp))
Notez que cela ne fonctionne pas si l'expression régulière contient des groupes non capturants et également si "(" est échappé en l'utilisant comme "[(]". Ce n'est donc pas très fiable. Mais en fonction des expressions régulières que vous utilisez, il peut Aidez-moi.
Cela peut être faux, mais je ne pense pas qu'il existe un moyen de trouver le nombre de groupes qui auraient été renvoyés si l'expression rationnelle avait été appariée. La seule façon dont je peux penser pour faire fonctionner cela comme vous le souhaitez est de passer le nombre de correspondances que votre regex attend comme argument.
Pour clarifier cependant: lorsque findall réussit, vous voulez seulement que la première correspondance soit retournée, mais quand il échoue, vous voulez une liste de chaînes vides? Parce que le commentaire semble montrer toutes les correspondances renvoyées sous forme de liste.
Utiliser votre code comme base:
def groups(regexp, s):
""" Returns the first result of re.findall, or an empty default
>>> groups(r'(\d)(\d)(\d)', '123')
('1', '2', '3')
>>> groups(r'(\d)(\d)(\d)', 'abc')
('', '', '')
"""
import re
m = re.search(regexp, s)
if m:
return m.groups()
return ('',) * len(m.groups())