2010-03-31 28 views
33

¿Existe alguna manera en Python para acceder a grupos de coincidencias sin crear explícitamente un objeto de coincidencia (u otra forma de embellecer el ejemplo siguiente)?Grupos de coincidencia en Python

Aquí es un ejemplo para aclarar mi motivación para la pregunta:

código Perl siguiente

if ($statement =~ /I love (\w+)/) { 
    print "He loves $1\n"; 
} 
elsif ($statement =~ /Ich liebe (\w+)/) { 
    print "Er liebt $1\n"; 
} 
elsif ($statement =~ /Je t\'aime (\w+)/) { 
    print "Il aime $1\n"; 
} 

traducido en Python

m = re.search("I love (\w+)", statement) 
if m: 
    print "He loves",m.group(1) 
else: 
    m = re.search("Ich liebe (\w+)", statement) 
    if m: 
    print "Er liebt",m.group(1) 
    else: 
    m = re.search("Je t'aime (\w+)", statement) 
    if m: 
     print "Il aime",m.group(1) 

se ve muy incómoda (if-else-cascada, hacer coincidir la creación de objetos).

+0

duplicado: http://stackoverflow.com/questions/122277/ how-do-you-translate-this-regular-expression-idiom-from-perl-into-python –

+0

Advertencia: Python re.match() específicamente coincide con el inicio del objetivo. Por lo tanto, re.match ("I love (\ w +)", "Oh! How I love thee") NO coincidiría. O desea usar re.search() o prefijar explícitamente la expresión regular con los patrones comodín apropiados para re.match (". * I love (\ w +)", ...) –

+0

@Jim Dennis: gracias a señalar; Adapte el ejemplo de python en consecuencia – Curd

Respuesta

31

Se puede crear un poco de clase que devuelve el resultado booleano de llamar partido, y retiene los grupos emparejados para posterior recuperación:

import re 

class REMatcher(object): 
    def __init__(self, matchstring): 
     self.matchstring = matchstring 

    def match(self,regexp): 
     self.rematch = re.match(regexp, self.matchstring) 
     return bool(self.rematch) 

    def group(self,i): 
     return self.rematch.group(i) 


for statement in ("I love Mary", 
        "Ich liebe Margot", 
        "Je t'aime Marie", 
        "Te amo Maria"): 

    m = REMatcher(statement) 

    if m.match(r"I love (\w+)"): 
     print "He loves",m.group(1) 

    elif m.match(r"Ich liebe (\w+)"): 
     print "Er liebt",m.group(1) 

    elif m.match(r"Je t'aime (\w+)"): 
     print "Il aime",m.group(1) 

    else: 
     print "???" 
+0

+1 buena solución; aunque es un poco detallado – Curd

+1

Puede ser detallado, pero pondrás la clase REMATCHER en un bonito módulo que importarás cuando sea necesario. No harías esta pregunta por un problema que no volverá a surgir en el futuro, ¿o sí? – tzot

+1

@ ΤΖΩΤΖΙΟΥ: Estoy de acuerdo; pero, ¿por qué aún no existe esa clase en el módulo? – Curd

13

menos eficiente, pero más simple de aspecto:

m0 = re.match("I love (\w+)", statement) 
m1 = re.match("Ich liebe (\w+)", statement) 
m2 = re.match("Je t'aime (\w+)", statement) 
if m0: 
    print "He loves",m0.group(1) 
elif m1: 
    print "Er liebt",m1.group(1) 
elif m2: 
    print "Il aime",m2.group(1) 

El problema con las cosas Perl es la actualización implícita de alguna variable oculta. Eso es simplemente difícil de lograr en Python porque necesita tener una declaración de asignación para actualizar realmente cualquier variable.

La versión con menos repetición (y mejor eficiencia) es esto:

pats = [ 
    ("I love (\w+)", "He Loves {0}"), 
    ("Ich liebe (\w+)", "Er Liebe {0}"), 
    ("Je t'aime (\w+)", "Il aime {0}") 
] 
for p1, p3 in pats: 
    m= re.match(p1, statement) 
    if m: 
     print p3.format(m.group(1)) 
     break 

una variación menor que alguna gente Perl prefieren:

pats = { 
    "I love (\w+)" : "He Loves {0}", 
    "Ich liebe (\w+)" : "Er Liebe {0}", 
    "Je t'aime (\w+)" : "Il aime {0}", 
} 
for p1 in pats: 
    m= re.match(p1, statement) 
    if m: 
     print pats[p1].format(m.group(1)) 
     break 

Ésta es apenas digno de mencionar excepto que llegar a veces de los programadores de Perl.

+3

@ S.Lott: bien, su solución evita la cascada if-else, pero a expensas de hacer coincidencias innecesarias (m1 y m2 no son necesarios si m0 coincide); Es por eso que no estoy realmente satisfecho con esta solución. – Curd

+2

+1 Me gusta mejor la segunda versión ... – Curd

+0

+1 para su segunda forma –

3

esto no es una solución regex.

alist={"I love ":""He loves"","Je t'aime ":"Il aime","Ich liebe ":"Er liebt"} 
for k in alist.keys(): 
    if k in statement: 
     print alist[k],statement.split(k)[1:] 
1

Se puede crear una función auxiliar:

def re_match_group(pattern, str, out_groups): 
    del out_groups[:] 
    result = re.match(pattern, str) 
    if result: 
     out_groups[:len(result.groups())] = result.groups() 
    return result 

y luego usarlo como esto:

groups = [] 
if re_match_group("I love (\w+)", statement, groups): 
    print "He loves", groups[0] 
elif re_match_group("Ich liebe (\w+)", statement, groups): 
    print "Er liebt", groups[0] 
elif re_match_group("Je t'aime (\w+)", statement, groups): 
    print "Il aime", groups[0] 

Es un poco torpe, pero hace el trabajo.

Cuestiones relacionadas