2008-09-24 28 views
20

¿Hay alguna manera de determinar cuántos grupos de captura hay en una expresión regular dada?Descubre el número de grupos de captura en las expresiones regulares de Python

Me gustaría ser capaz de hacer lo siguientes aparatos:

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) 

Esto me permite hacer cosas como:

first, last, phone = groups(r'(\w+) (\w+) ([\d\-]+)', 'John Doe 555-3456') 

Sin embargo, no sé cómo implementar num_of_groups. (Actualmente sólo trabajo a su alrededor.)

EDIT: Después de laadvice from rslite, he sustituido re.findall con re.search.

sre_parse parece la solución más robusta y completa, pero requiere un recorrido de árbol y parece ser un poco pesado.

La expresión regular de MizardX parece cubrir todas las bases, así que voy a ir con eso.

Respuesta

29
def num_groups(regex): 
    return re.compile(regex).groups 
+5

Esto no devolverá la cantidad de grupos, devolverá una tupla de todos los grupos. Para devolver el número de grupos, necesitará el siguiente código (en Python 3.4): 'return len (re.compile (regex) .groups())' –

+2

@RaziShaban 're.compile (regex) .groups 'es una propiedad de tipo' int'. 're.compile (regex) .match (input) .groups()' es un método que devuelve 'tuple'. –

+0

bien, lo siento, estaba trabajando con Findall, no compilar. El punto que estaba haciendo es que su función se llama 'num_groups' pero devuelve una tupla, no un número. –

1

En primer lugar, si solo necesita el primer resultado de re.findall, es mejor usar re.search que devuelva una coincidencia o None.

Para los grupos de números que podría contar el número de paréntesis de apertura '(' excepto aquellos que se escapó por '\' Se podría utilizar otra expresión regular para que:.

def num_of_groups(regexp): 
    rg = re.compile(r'(?<!\\)\(') 
    return len(rg.findall(regexp)) 

Tenga en cuenta que esto no lo hace funciona si la expresión regular contiene grupos que no capturan y también si '(' se escapó usándolo como '[(]'. Esto no es muy confiable. Pero dependiendo de las expresiones regulares que use puede ayudar.

2

La propiedad lastindex del objeto partido debe ser lo que está buscando. Consulte el re module docs.

+0

Si no se encuentra ninguna coincidencia, no tengo un objeto partido. Además, no creo que eso sea lo que lastindex hace. – itsadok

2

Algo dentro de sre_parse podría ayudar.

A primera vista, tal vez algo en la línea de:

>>> 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')])]))] 

es decir, contar los elementos de tipo 'sub-patrón':

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']) 

en cuenta que sólo estamos contando los patrones de nivel de raíz, así que el último ejemplo sólo devuelve 1. Para cambiar esto, fichas necesitarían buscado de forma recursiva.

0

Podría ser incorrecto, pero no creo que haya una manera de encontrar la cantidad de grupos que se habrían devuelto si la expresión regular hubiera coincidido. La única forma en que puedo pensar para hacer que esto funcione de la manera que quieres es pasar el número de coincidencias que tu expresión regular particular espera como argumento.

Sin embargo, para aclarar: ¿Cuándo findall tiene éxito, solo desea que se devuelva la primera coincidencia, pero cuando falla, desea una lista de cadenas vacías? Porque el comentario parece mostrar todas las coincidencias que se devuelven como una lista.

0

Utilizando su código como 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()) 
+0

Esto arrojará una excepción cuando no se encuentre ninguna coincidencia – itsadok

0
f_x = re.search(...) 
len_groups = len(f_x.groups()) 
Cuestiones relacionadas