Возможно ли получить произвольный неупорядоченный набор именованных групп одним махом с помощью модуля Python re? - PullRequest
6 голосов
/ 03 февраля 2010

Это очень удобно для некоторых проблем:

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

Но что, если я не знаю, какой порядок ожидать раньше времени?

[update]

Например, скажем, у меня есть входная переменная, содержащая некоторый неизвестный порядок символов, и так уж случилось, что «b» следует после «i».Я все еще хочу иметь возможность ссылаться на группы для ".b."и я.'без необходимости заказывать мои регулярные выражения в соответствии с их порядком на входе var.Итак, я бы хотел сделать что-то подобное, но я не знаю, возможно ли это:

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

[конец обновления]

Я искал вокруг и ломал головуно не может генерировать хорошие выводы.Предполагать, что эта функциональность не будет существовать, потому что, вероятно, единственный способ сделать это - отсканировать всю строку один раз для каждой группы (что, конечно, я мог бы сделать в цикле вместо этого), но я подумал, что увижу, что такое мозговой стекдолжен был сказать об этом.

Спасибо за помощь,
Джош

Ответы [ 5 ]

1 голос
/ 03 февраля 2010

Используйте вертикальную черту ("или") в шаблоне RE и finditer, чтобы получить все интересующие объекты совпадения: у каждого будет groupdict с None в качестве значения для групп, не участвующих в этом совпадении и вы можете «объединять» диктовки по своему усмотрению.

Например:

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

при этом используется стратегия слияния, которая заключается просто в выборе первого фактического соответствия для каждой именованной группы в шаблоне. Теперь например

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

предположительно, по вашему желанию, если я правильно интерпретирую ваш вопрос.

0 голосов
/ 05 августа 2017

Вот поздний пришедший в игру одним движением, которое также доступно для начинающих:

>>> dict([(name, re.search(pattern, "abcdefghijk").group())
          for name, pattern in {"b": ".b.", "i": ".i"}.items()])  
{'b': 'abc', 'i': 'hij'}
0 голосов
/ 27 февраля 2013

Вот способ, который не требует finditer ни слияния словаря:

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

Это предполагает, что каждый из символов b и i появляется в вашей строке ровно один раз, в противном случае:

  • Если один из символов может отсутствовать, вы можете использовать {,2} вместо {2}.
  • Если один из символов появляется более одного раза, поиск извлекает первые два появления или из них (например, он может найти b дважды и вообще не найти i) .
0 голосов
/ 03 февраля 2010

Самое близкое, что я могу получить, это:

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

То, как вы комбинируете словари, зависит от того, ожидаете ли вы более одного совпадения.Если вам нужно только одно совпадение, вы можете сделать:

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

Или для нескольких совпадений:

>>> 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 голосов
/ 03 февраля 2010
>>> [m.groupdict() for m in re.finditer('(?P<b>.b.)|(?P<i>.i.)', 'abcdefghijk')]
[{'i': None, 'b': 'abc'}, {'i': 'hij', 'b': None}]

Кажется, работает нормально, хотя, если у вас много групп, проверка, какая из них не является None, может стать утомительной.

Это находит все .b. и все .i. совпадения встрока.Если вы хотите быть уверены, что каждый найдет по одному, вам придется проверить это и вручную.

...