能否使用Python的re模块一次性获取任意无序的命名组集合?

7 投票
5 回答
1078 浏览
提问于 2025-04-15 18:48

这个方法在解决某些问题时非常方便:

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

但是如果我事先不知道字符的顺序该怎么办呢?

[更新]

比如说,我有一个输入变量,里面包含一些顺序未知的字符,恰好'b'在'i'后面。我希望仍然能够引用'.b.'和'.i.'这两个组,而不需要根据它们在输入变量中的顺序来排列我的正则表达式。所以,我希望能做类似这样的事情,但我不知道这是否可能:

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

[更新结束]

我搜索了很多资料,绞尽脑汁也没有找到好的线索。我猜这个功能可能不存在,因为正则表达式要做到这一点,可能需要对每个组扫描整个字符串一次(当然我可以用循环来实现),但我想看看StackOverflow上的高手们对此有什么看法。

谢谢你的帮助,
Josh

相关问题:

5 个回答

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

我能做到的最接近的就是这个:

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

在正则表达式的模式中使用竖线("或"),然后用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'}

如果我理解你的问题没错的话,这样应该是你想要的。

撰写回答