Possível recuperar um conjunto arbitrário de grupos nomeados em uma merda com o módulo RE do Python?

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

  •  25-09-2019
  •  | 
  •  

Pergunta

Isso é super útil para alguns problemas:

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

Mas e se eu não souber que ordem esperar antes do tempo?

atualizar

Por exemplo, digamos que eu tenho uma variável de entrada contendo alguma ordem desconhecida de caracteres e acontece que 'B' vem depois de 'i'. Eu ainda quero poder fazer referência aos grupos para '.b'. e eu.' sem ter que pedir meu regex de acordo com a ordem deles na entrada var. Então, eu gostaria de poder fazer algo assim, mas não sei se é possível:

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

Atualização final

Eu procurei e acalmei um monte de meu cérebro, mas não posso gerar bons leads. Descobrindo que essa funcionalidade não existiria, porque provavelmente a única maneira de fazer isso é escanear a corda inteira uma vez para cada grupo (o que, é claro, eu poderia fazer em um loop), mas pensei em ver o que o cérebro do Stackoverflow Tive que dizer sobre isso.

Obrigado pela ajuda,
Josh

Foi útil?

Solução

Use uma barra vertical ("ou") no padrão de re e finditer Para obter todos os objetos de interesse correspondentes: cada um terá um groupdict com None Como o valor para os grupos não envolvidos nessa correspondência, e você pode "mesclar" os ditos como preferir.

Por exemplo:

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

Isso usa uma estratégia de mesclagem que é apenas para escolher a primeira correspondência real para cada grupo nomeado no padrão. Agora, por exemplo

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

Presumivelmente, como você deseja, se eu interpretar sua pergunta corretamente.

Outras dicas

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

Parece funcionar bem, embora se você tenha muitos grupos verificando qual não None Pode ser entediante.

Isso encontra tudo .b. e tudo .i. combina na string. Se você quisesse ter certeza de que encontrou um de cada um, terá que verificar isso manualmente também.

O mais próximo que posso conseguir é o seguinte:

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

Como você combina os dicionários depende se você está esperando mais de uma partida. Se você quiser apenas uma correspondência cada uma, você pode fazer:

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

Ou para várias correspondências:

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

Aqui está uma maneira que não requer finditer nem fusão de dicionário:

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

Isso está assumindo cada um dos personagens b e i aparece exatamente uma vez na sua string, caso contrário:

  • Se um dos personagens pode estar faltando, você pode usar {,2} ao invés de {2}.
  • Se um dos personagens aparecer mais de uma vez, a pesquisa recuperará as duas primeiras aparições de qualquer deles (por exemplo, pode encontrar b duas vezes e não encontrar i de forma alguma).

Aqui está um canto tardio para o jogo em um toque, que também é legível para iniciantes:

>>> dict([(name, re.search(pattern, "abcdefghijk").group())
          for name, pattern in {"b": ".b.", "i": ".i"}.items()])  
{'b': 'abc', 'i': 'hij'}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top