我有文件 abc.txt
abc/pqr/lmn/xyz:pass
abc/pqr/lmn/bcd:pass
我需要解析这些语句,输出应该在嵌套字典中,如下所示
{'abc':{'pqr':{'lmn':{'xyz':{'pass':1},{'bcd':{'pass':1}}}}}}
其中1是通过计数。 我能够做到这一点
import re
d={}
p=re.compile('[a-zA-z]+')
for line in open('abc.txt'):
for key in p.findall(line):
d['key']={}
我是python的新手,所以任何人都可以帮助我完成这个
答案 0 :(得分:3)
这是我的答案的更新版本,其中树数据结构的叶子现在与其余部分的叶子不同。而不是树严格地是dict
- 嵌套 - dict
s,"离开"现在,每个分支上都有一个名为collections.Counter
的dict
的不同子类的实例,这些子类可用于计算每个键发生的次数。我之所以这样做,是因为你回答了我的问题,如果每一行的最后一部分不是":pass"
,那将会发生什么(这是"我们必须为该键添加新计数")
嵌套字典通常称为 Tree
数据结构,可以递归定义 - 根是字典,也可以是字典。以下使用dict
子类而不是普通dict
,因为它使构造它们更容易,因为您不需要特殊情况下创建下一级的第一个分支(除了我仍然在添加" leaves"时会这样做,因为它们是不同的子类collections.Counter
)。
from collections import Counter
import re
# (optional) trick that redefines Counter subclass to print like a regular dict
class Counter(Counter):
def __repr__(self):
return dict(self).__repr__()
# borrowed from answer @ http://stackoverflow.com/a/19829714/355230
class Tree(dict):
def __missing__(self, key):
value = self[key] = type(self)()
return value
# some functions based on answer @ http://stackoverflow.com/a/14692747/355230
def nested_dict_get(nested_dict, keys):
return reduce(lambda d, k: d[k], keys, nested_dict)
def nested_dict_set(nested_dict, keys, value):
nested_dict_get(nested_dict, keys[:-1])[keys[-1]] = value
def nested_dict_update_count(nested_dict, keys):
if nested_dict_get(nested_dict, keys[:-1]): # update existing Counter
nested_dict_get(nested_dict, keys[:-1]).update([keys[-1]])
else: # create a new Counter
nested_dict_set(nested_dict, keys[:-1], Counter([keys[-1]]))
d = Tree()
pat = re.compile(r'[a-zA-z]+')
with open('abc.txt') as file:
for line in file:
nested_dict_update_count(d, [w for w in pat.findall(line.rstrip())])
print d # prints like a regular dict
为了测试修订代码的叶子计数功能,我使用了以下测试文件,该文件包含两次相同的行,一次以:pass
结束,另一次以:fail
结尾。
扩展abc.txt
测试文件:
abc/pqr/lmn/xyz:pass
abc/pqr/lmn/bcd:pass
abc/pqr/lmn/xyz:fail
abc/pqr/lmn/xyz:pass
输出:
{'abc': {'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'fail': 1, 'pass': 2}}}}}
如果您对有关计算每行的最后一个字的评论的正确解释,请告诉我。
答案 1 :(得分:1)
查看字典上的setdefault
方法。
d = {}
d.setdefault('pqr', {}).setdefault('lmn', {}).setdefault('xyz', {})['pass'] = 1
d.setdefault('pqr', {}).setdefault('lmn', {}).setdefault('bcd', {})['pass'] = 1
d
给出
{'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'pass': 1}}}}
答案 2 :(得分:0)
如果我理解你的问题:
sources = ["abc/pqr/lmn/xyz:pass", "abc/pqr/lmn/bcd:pass", "abc/pqr/lmn/xyz:pass"]
def prepare_source(source):
path, value = source.split(':')
elements = path.split('/')
return elements, value
def add_key(elements, value):
result = dict()
if len(elements) > 1:
result[elements[0]] = add_key(elements[1:], value)
else:
result[elements[0]] = {value: 1}
return result
# base merge function get from here:
# http://stackoverflow.com/questions/7204805/dictionaries-of-dictionaries-merge
def merge(a, b, path=None):
"merges b into a"
if path is None: path = []
for key in b:
if key in a:
if isinstance(a[key], dict) and isinstance(b[key], dict):
merge(a[key], b[key], path + [str(key)])
elif isinstance(a[key], int) and isinstance(b[key], int):
a[key] += b[key]
else:
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
else:
a[key] = b[key]
return a
result = dict()
for source in sources:
result = merge(result, add_key(*prepare_source(source)))
print result
输出将是:
{'abc': {'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'pass': 2}}}}}