Possibilità di recuperare un insieme non ordinato arbitrario di gruppi denominati in un colpo solo con il modulo re di Python?

StackOverflow https://stackoverflow.com/questions/2187931

  •  25-09-2019
  •  | 
  •  

Domanda

Questa è super utile per alcuni problemi:

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

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

[update]

Per esempio, dire che ho una variabile di ingresso contenente un ordine sconosciuto di caratteri e si dà il caso che la 'b' viene dopo 'i'. Voglio essere ancora in grado di fare riferimento ai gruppi di '.B.' e io.' senza dover ordinare il mio regex in base al loro ordine nella var di ingresso. 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 travasato il mio cervello un gruppo, ma non è in grado di generare qualsiasi porta buoni. Indovinare questa funzionalità non esisterebbe, perché probabilmente l'unico modo per il riutilizzo per farlo è quello di eseguire la scansione l'intera stringa una volta per ogni gruppo (che naturalmente ho potuto fare in un ciclo invece) ma ho pensato che avrei visto ciò che il cervello StackOverflow aveva da dire in proposito.

Grazie per il vostro aiuto,
Josh

È stato utile?

Soluzione

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ò "merge "i dicts, come si preferisce.

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 volete, se interpreto correttamente la tua domanda.

Altri suggerimenti

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

Questa trova tutto .b. e tutte le partite .i. nella stringa. Se si voleva essere sicuri che uno ha trovato uno di ciascuno si dovrà verificare che manualmente, anche.

Il più vicino che posso 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 solo una 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']})

Ecco un modo che non richiede né finditer fusione 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'}

Ciò presuppone ognuno dei personaggi b e i appare esattamente una volta nella stringa, altrimenti:

  • Se uno dei personaggi potrebbe mancare, è possibile utilizzare al posto di {,2} {2}.
  • Se uno dei personaggi appare più di una volta, la ricerca recupererà le prime due apparizioni di o di loro (ad esempio si può trovare b due volte e non trovare i affatto).

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'}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top