2010-02-02 5 views
6

Esto es súper práctico para algunos problemas:¿Es posible recuperar un conjunto desordenado arbitrario de grupos nombrados de una sola vez con el módulo re de Python?

>>> re.search('(?P<b>.b.).*(?P<i>.i.)', 'abcdefghijk').groupdict() 
{'i': 'hij', 'b': 'abc'} 

Pero lo que si no sé qué orden puede esperar antes de tiempo?

[Actualización]

Por ejemplo, decir que tengo una variable de entrada que contiene un poco de orden desconocido de personajes y da la casualidad de que 'b' se produce después de 'i'. Quiero poder seguir haciendo referencia a los grupos para '.b'. y yo.' sin tener que pedir mi expresión regular de acuerdo con su orden en la entrada var. Por lo tanto, me gustaría poder hacer algo como esto, pero no sé si es posible:

>>> re.search('(?P<b>.b.)|(?P<i>.i.)', unknown_order_alphabet_str).groupdict() 
{'i': 'hij', 'b': 'abc'} 

[actualización final]

He buscado alrededor y devanaba los sesos un montón, pero no puede generar buenas pistas. Adivinar esta funcionalidad no existiría porque probablemente la única forma de hacer esto es escanear toda la cadena una vez para cada grupo (lo que por supuesto podría hacer en un ciclo) pero pensé que vería qué es el flujo de stackoverflow tenía que decir al respecto.

Gracias por su ayuda,
Josh

Respuesta

1

Utilice una barra vertical ("o") en el patrón RE, y finditer para conseguir todos los objetos de los partidos de interés: cada uno tendrá un groupdict con None como el valor para los grupos que no participan en ese partido, y se puede " fusionar "los dictados como prefiera".

Por ejemplo:

import re 

def mergedgroupdict(pattern, thestring): 
    there = re.compile(pattern) 
    result = {} 
    for mo in there.finditer(thestring): 
    d = mo.groupdict() 
    for k in d: 
     if k not in result and d[k] is not None: 
     result[k] = d[k] 
    return result 

este utiliza una estrategia de combinación que es sólo para recoger el primer partido actual de cada grupo nombrado en el patrón. Ahora, por ejemplo,

>>> mergedgroupdict('(?P<b>.b.)|(?P<i>.i.)', 'abcdefghijk') 
{'i': 'hij', 'b': 'abc'} 
>>> mergedgroupdict('(?P<b>.b.)|(?P<i>.i.)', 'abcdefghijk'[::-1]) 
{'i': 'jih', 'b': 'cba'} 

presumiblemente como lo desee, si interpreto su pregunta correctamente.

+0

Hay una manera que no requiere 'finditer' ni fusión de diccionarios. Ver [mi respuesta] (http://stackoverflow.com/a/15112978/566639). – Joe

0
>>> [m.groupdict() for m in re.finditer('(?P<b>.b.)|(?P<i>.i.)', 'abcdefghijk')] 
[{'i': None, 'b': 'abc'}, {'i': 'hij', 'b': None}] 

parece funcionar bien, aunque si tiene muchos grupos de verificación, que uno no se None podría resultar tedioso.

Encuentra todas las coincidencias .b. y .i. en la cadena. Si quiere asegurarse de que encuentre uno de cada uno, deberá verificarlo manualmente también.

+0

Gracias mucho por su ayuda! –

+0

Hay una manera que no requiere la fusión de 'finditer' ni diccionario. Ver [mi respuesta] (http://stackoverflow.com/a/15112978/566639). – Joe

0

El más cercano que puedo conseguir es la siguiente:

>>> [match.groupdict() for match in re.finditer('(?P<b>.b.)|(?P<i>.i.)', 'abcdefghijk')] 
[{'i': None, 'b': 'abc'}, {'i': 'hij', 'b': None}] 

¿Cómo se combinan los diccionarios depende entonces de si usted está esperando más de una coincidencia.Si sólo desea un partido cada uno, usted podría hacer:

>>> results = {} 
>>> for match in re.finditer('(?P<b>.b.)|(?P<i>.i.)', 'abcdefghijk'): 
...  results.update(dict((k,v) for k, v in match.groupdict().iteritems() if v is not None)) 
... 
>>> results 
{'i': 'hij', 'b': 'abc'} 

o para varios partidos:

>>> results = defaultdict(lambda: []) 
>>> for match in re.finditer('(?P<b>.b.)|(?P<i>.i.)', 'abcdefghijkabcdefghijk'): 
...  for k, v in match.groupdict().iteritems(): 
...   if v is not None: 
...    results[k].append(v) 
... 
>>> results 
defaultdict(<function <lambda> at 0x7f53d0992c08>, {'i': ['hij', 'hij'], 'b': ['abc', 'abc']}) 
+0

Hay una manera que no requiere 'finditer' ni fusión de diccionarios. Ver [mi respuesta] (http://stackoverflow.com/a/15112978/566639). – Joe

0

Aquí está una manera que no requiere ni finditer diccionario de fusión:

>>> pat = re.compile(r'(?:.*?(?:(?P<b>.b.)|(?P<i>.i.))){2}') 

>>> pat.search('abcdefghijk').groupdict() 
{'i': 'hij', 'b': 'abc'} 

>>> pat.search('aicdefghbjk').groupdict() 
{'i': 'aic', 'b': 'hbj'} 

Esto supone que cada uno de los caracteres b y i aparece exactamente una vez en su cadena, de lo contrario:

  • Si falta uno de los caracteres, puede usar {,2} en lugar de {2}.
  • Si uno de los caracteres aparece más de una vez, la búsqueda recuperará los primeros dos aspectos de ya sea (por ejemplo, puede encontrar b dos veces y no encontrar i en absoluto).
0

Aquí es un recién llegado al juego de un golpe, que es legible para los principiantes también:

>>> dict([(name, re.search(pattern, "abcdefghijk").group()) 
      for name, pattern in {"b": ".b.", "i": ".i"}.items()]) 
{'b': 'abc', 'i': 'hij'} 
Cuestiones relacionadas