2010-01-08 29 views
5

Necesito probar una cadena contra varias expresiones regulares (exclusivas, es decir, una cadena que coincide con una de ellas no puede coincidir con ninguna otra), y ejecutar una pieza diferente de código dependiendo de con qué coincida. Lo que tengo actualmente es:¿Cómo busco a través de las coincidencias de expresiones regulares en Python?

m = firstre.match(str) 
if m: 
    # Do something 

m = secondre.match(str) 
if m: 
    # Do something else 

m = thirdre.match(str) 
if m: 
    # Do something different from both 

Aparte de la fealdad, el código coincide en contra de todas las expresiones regulares, incluso después de que ha emparejado a uno de ellos (por ejemplo firstre), que es ineficiente. Traté de usar:

elif m = secondre.match(str) 

pero descubrí que la asignación no está permitida en las sentencias if.

¿Hay una forma elegante de lograr lo que quiero?

Respuesta

4
def doit(s): 

    # with some side-effect on a 
    a = [] 

    def f1(s, m): 
     a.append(1) 
     print 'f1', a, s, m 

    def f2(s, m): 
     a.append(2) 
     print 'f2', a, s, m 

    def f3(s, m): 
     a.append(3) 
     print 'f3', a, s, m 

    re1 = re.compile('one') 
    re2 = re.compile('two') 
    re3 = re.compile('three') 


    func_re_list = (
     (f1, re1), 
     (f2, re2), 
     (f3, re3), 
    ) 
    for myfunc, myre in func_re_list: 
     m = myre.match(s) 
     if m: 
      myfunc(s, m) 
      break 


doit('one') 
doit('two') 
doit('three') 
+0

+1 por genialidad ptónica pura. Personalmente, colocaría la lista de tuplas fuera de la declaración for, p. 'match_functions = ((f1, re1), (f2, re2), ..)' y do 'for myfunc, myre in match_functions:' – Kimvais

+1

No olvides agregar "break" para guardar intentando coincidir con el resto de la lista. –

+0

Editado con las sugerencias de los comentarios más un ejemplo real. –

1

Algunas ideas, ninguna de ellas buena necesariamente, pero podría adaptarse a su código así:

¿Qué hay de poner el código en una función separada, es decir MatchRegex(), que devuelve el cual coincidía con regex. De esa forma, dentro de la función, puede usar una devolución después de que haya coincidido con la primera (o segunda) expresión regular, lo que significa que pierde la ineficiencia.

Por supuesto, siempre se puede ir con if declaraciones simplemente anidados:

m = firstre.match(str) 
if m: 
    # Do something 
else: 
    m = secondre.match(str) 
    ... 

Realmente no ven ninguna razón para no ir con anidados if s. Son muy fáciles de entender y tan eficientes como usted desee. Yo iría por ellos solo por su simplicidad.

+0

+1 por sugerir la solución simple para el problema –

+0

¿y si hay unos pocos cientos de expresiones regulares? el código sería difícil de leer por más de 10 y algo. – kibitzer

+0

@kibitzer: En ese caso, tiene sentido diseñar una solución general. O en el caso donde se espera que crezca a eso. No cada vez que tiene que escribir 3 if anidados. –

3

Esto podría ser un poco más de ingeniería de la solución, pero podría combinarlos como una sola expresión regular con grupos nombrados y ver qué grupo coincide. Esto podría ser encapsulado como una clase de ayuda:

import re 
class MultiRe(object): 
    def __init__(self, **regexps): 
     self.keys = regexps.keys() 
     self.union_re = re.compile("|".join("(?P<%s>%s)" % kv for kv in regexps.items())) 

    def match(self, string, *args): 
     result = self.union_re.match(string, *args) 
     if result: 
      for key in self.keys: 
       if result.group(key) is not None: 
        return key 

de búsqueda sería así:

multi_re = MultiRe(foo='fo+', bar='ba+r', baz='ba+z') 
match = multi_re.match('baaz') 
if match == 'foo': 
    # one thing 
elif match == 'bar': 
    # some other thing 
elif match == 'baz': 
    # or this 
else: 
    # no match 
+0

¡Agradable! (min 15 char) –

+0

Esto revisa la ingeniería desde mi punto de vista. No encuentro el código realmente fácil de entender. –

0

Los primeros resultados, tal vez?

def doit(s): 
    m = re1.match(s) 
    if m: 
     # Do something 
     return 

    m = re2.match(s) 
    if m: 
     # Do something else 
     return 

    ... 

Hormigas La respuesta de Aasma es buena también. Si prefiere menos andamios, puede escribirlo usted mismo usando el verbose regex syntax.

re = re.compile(r'''(?x) # set the verbose flag 
    (?P<foo> fo+) 
    | (?P<bar> ba+r) 
    | #...other alternatives... 
''') 

def doit(s): 
    m = re.match(s) 
    if m.group('foo'): 
     # Do something 
    elif m.group('bar'): 
     # Do something else 
    ... 

He hecho esto mucho. Es rápido y funciona con re.finditer.

0

lo hacen con un elif en caso de que sólo se necesita un verdadero/falso de coincidencia de expresiones regulares:

if regex1.match(str): 
    # do stuff 
elif regex2.match(str): 
    # and so on 
+1

Creo que necesita el valor de retorno de regex.match (str) –

1

usted podría utilizar

def do_first(str, res, actions): 
    for re,action in zip(res, actions): 
    m = re.match(str) 
    if m: 
     action(str) 
     return 

Así, por ejemplo, digamos que usted ha definido

def do_something_1(str): 
    print "#1: %s" % str 

def do_something_2(str): 
    print "#2: %s" % str 

def do_something_3(str): 
    print "#3: %s" % str 

firstre = re.compile("foo") 
secondre = re.compile("bar") 
thirdre = re.compile("baz") 

a continuación, llame con

do_first("baz", 
     [firstre,  secondre,  thirdre], 
     [do_something_1, do_something_2, do_something_3]) 
3

Esta es una buena aplicación para la clase re.Scanner indocumentada pero bastante útil.

+0

¡Agradable! Gracias por el enlace. – Brandon

Cuestiones relacionadas