2010-02-02 12 views
6

Questo è super utile per alcuni problemi:È possibile recuperare un insieme arbitrario non ordinato di gruppi con nome in un unico colpo con il modulo re di Python?

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

Ma cosa succede se io non so cosa per aspettarsi prima del tempo?

[aggiornamento]

esempio, dire che ho una variabile di ingresso contenente un ordine sconosciuto di caratteri, così succede che 'b' viene dopo 'i'. Voglio ancora poter fare riferimento ai gruppi per ".b". e io.' senza dover ordinare il mio regex in base al loro ordine nella variabile var. Quindi, vorrei poter fare qualcosa di simile, ma non so se è possibile:

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

[update fine]

Ho cercato in giro e tormentato il mio cervello un gruppo, ma non può generare qualsiasi buona porta. Indovinare questa funzionalità non esisterebbe perché probabilmente l'unico modo per farlo è scansionare l'intera stringa una volta per ogni gruppo (che ovviamente potrei fare in un ciclo invece) ma ho pensato di vedere cosa il cervello dello stackoverflow dovevo dire al riguardo.

Grazie per il vostro aiuto,
Josh

risposta

1

Utilizzare una barra verticale ("o") nel modello RE, e finditer per ottenere tutti gli oggetti di corrispondenza di interesse: ciascuno avrà un groupdict con None come il valore per i gruppi non coinvolti in quella partita, e si può " unisci "i dadi come preferisci.

Ad esempio:

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 

questo utilizza una strategia di fusione che è solo per prendere la prima partita reale per ciascun gruppo con nome nel modello. Ora per esempio

>>> 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'} 

presumibilmente come desideri, se interpreto correttamente la tua domanda.

+0

Esiste un modo che non richiede il 'finditer' o la fusione dei dizionari. Vedi [la mia risposta] (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}] 

sembra funzionare bene, anche se si dispone di molti gruppi di controllo che non è None potrebbe diventare noioso.

Questo trova tutte le .b. e tutte le corrispondenze .i. nella stringa. Se vuoi essere sicuro che ne trovi uno, dovrai controllarlo manualmente anche tu.

+0

Grazie mille per il vostro aiuto! –

+0

Esiste un modo che non richiede la fusione di "finder' o dizionario". Vedi [la mia risposta] (http://stackoverflow.com/a/15112978/566639). – Joe

0

Il più vicino che può ottenere è questo:

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

Come si combinano i dizionari poi dipende dal fatto che ci si aspetta più di una corrispondenza.Se si desidera una sola partita ciascuno, si potrebbe fare:

>>> 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 per più corrispondenze:

>>> 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

Esiste un modo che non richiede la fusione di finder' o dizionario. Vedi [la mia risposta] (http://stackoverflow.com/a/15112978/566639). – Joe

0

Ecco un modo che non richiede la fusione finditer né dizionario:

>>> 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'} 

Si presume che ognuno dei caratteri b e i appaia esattamente una volta nella stringa, altrimenti:

  • Se uno dei caratteri potrebbe mancare, è possibile utilizzare {,2} anziché {2}.
  • Se uno dei caratteri appare più di una volta, la ricerca recupera i primi due aspetti di o di essi (ad esempio può trovare b due volte e non trovare i per niente).
0

Ecco un ritardatario al gioco in un colpo, che è leggibile anche per i principianti:

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