2010-02-02 3 views
6

Это очень удобно для некоторых проблем:Возможно получить произвольный неупорядоченный набор именованных групп одним махом с модулем re-python?

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

Но что, если я не знаю, что для того, чтобы ожидать раньше времени?

[обновление]

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

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

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

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

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

ответ

1

Используйте вертикальную полосу («или») в структуре 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

Существует способ, который не требует слияния словаря или словаря. См. [Мой ответ] (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}] 

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

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

+0

Большое спасибо за вашу помощь! – 2010-02-02 22:19:29

+0

Существует способ, который не требует слияния словаря или поиска словаря. См. [Мой ответ] (http://stackoverflow.com/a/15112978/566639). – Joe

0

Ближайший я могу получить это:

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

Существует способ, который не требует слияния словаря или поиска словаря. См. [Мой ответ] (http://stackoverflow.com/a/15112978/566639). – Joe

0

Вот способ, который не требует ни 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

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

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