能否使用Python的re模块一次性获取任意无序的命名组集合?
这个方法在解决某些问题时非常方便:
>>> 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'}
如果我理解你的问题没错的话,这样应该是你想要的。