基于 .properties 文件制作 python 字典
Crafting a python dictionary based on a .properties file
我想将 .properties 文件的键和值解析为 python 字典。
我正在解析的 .properties-file 使用以下语法(键和值是示例):
key1.subkey1.subsubkey1=value1
key1.subkey1.subsubkey2=value2
key1.subkey2=value3
key2=value4
因此每个值都对应一个键,键由一个或多个用句点分隔的级别组成。
目标是创建一个 Python 字典,其中每个键都是一个包含其值和子键的字典。字典应该是递归迭代的,所以每一层都应该遵循相同的结构。
前面的示例应生成以下类型的字典:
'subKeys':
'key1':
'subKeys':
'subkey1':
'subKeys':
'subsubkey1':
'val': 'value1'
'subsubkey2':
'val': 'value2'
'subkey2':
'val': 'value3'
'key2':
'val': 'value4'
我在 python 中使用以下算法循环它:
def setKeyAndValue(storageDict, rowParts):
keyParts = rowParts[0].split('.')
if not keyParts[0] in outputDict:
storageDict[keyParts[0]] = {}
newObj = storageDict[keyParts[0]]
for i in range(len(keyParts)):
if i == len(keyParts)-1:
# Reached the end of the key, save value to dictionary
newObj["val"] = rowParts[1]
else :
# Not yet at the end of the key
if "subKeys" not in newObj:
newObj["subKeys"] = {}
if keyParts[i+1] not in newObj["subKeys"]:
newObj["subKeys"][keyParts[i+1]] = {}
newObj = newObj["subKeys"][keyParts[i+1]]
f = open("FILEPATH.properties", "r")
outputDict = {}
outputDict["subKeys"] = {}
outputDictSubKeys = outputDict["subKeys"]
for row in f:
if not row.startswith('#') and not row.startswith('//'):
parts = row.split('=', 1)
if len(parts)== 2:
setKeyAndValue(outputDictSubKeys, parts)
f.close()
生成的字典 (outputDict) 缺少两个键值对 (key1.subkey1.subsubkey1=value1,
key1.subkey1.subsubkey2=value2):
'subKeys':
'key1':
'subKeys':
'subkey2':
'val': 'value3'
'key2':
'val': 'value4'
我很确定问题出在下一行:
newObj = newObj["subKeys"][keyParts[i+1]]
我在循环的每次迭代中替换字典中的 newObj。
有没有办法调整这个现有的算法使其工作,如果没有,我应该如何重新开始?效率不是问题,属性文件不是很大。
一些观察:
解决方案
数据
现在假设我们有一个名为 data.properties
的文件,其结构如下
key1.subkey1.subsubkey1=value1
key1.subkey1.subsubkey2=value2
key1.subkey2=value3
key2=value4
代码
那么我们可以使用下面的代码
import functools
from collections import defaultdict
from pprint import pprint
if __name__ == '__main__':
node = lambda: defaultdict(node)
trie = node()
with open("data.properties", 'r') as file:
for line in file.readlines():
key, value = line.strip().split('=')
functools.reduce(dict.__getitem__, key.split('.'), trie)
val = (functools.reduce(lambda d, key: d.get(key), key.split('.')[:-1], trie))
val[key.split('.')[-1]] = value
pprint(trie)
输出
生成以下输出
defaultdict(<function <lambda> at 0x000002054579EF70>,
{'key1': defaultdict(<function <lambda> at 0x000002054579EF70>,
{'subkey1': defaultdict(<function <lambda> at 0x000002054579EF70>,
{'subsubkey1': 'value1',
'subsubkey2': 'value2'}),
'subkey2': 'value3'}),
'key2': 'value4'})
其中defaultdict
可以忽略,只要key在字典中即可。如果要去掉defaultdict
属性,可以用这个函数
def defaultdict_to_dict(data):
""" Convert a nested defaultdict to a normal dictionary. """
if isinstance(data, defaultdict):
data = dict(data)
if isinstance(data, dict):
for k, v in data.items():
data[k] = defaultdict_to_dict(v)
return data
并使用 pprint(defaultdict_to_dict(trie))
调用它。
这将输出
{'key1': {'subkey1': {'subsubkey1': 'value1',
'subsubkey2': 'value2'},
'subkey2': 'value3'},
'key2': 'value4'}
说明
大部分魔术都发生在以下两行中
1)
functools.reduce(dict.__getitem__, key.split('.'), trie)
2)
val = (functools.reduce(lambda d, key: d.get(key), key.split('.')[:-1], trie))
在第一行中,我们为所有键创建 Trie,所有值(叶子)都将是 defaultdict
。这意味着它可以扩展到任意深度(拥有任意多的子键)。
在第二行中,我们正在遍历 Trie,按照键,直到最后一个键。以 key1.subkey1.subsubkey1=value1
为例,此代码将类似于
val = trie['key1']['subkey1']
下一行 (val[key.split('.')[-1]] = value
) 等于
trie['key1']['subkey1']['subsubkey1'] = 'value1'
我复制了你的函数并测试了你的代码并做了一些修改。以下代码运行良好。
def setKeyAndValue(storageDict, rowParts):
print rowParts
keyParts = rowParts[0].split('.')
if not keyParts[0] in storageDict.keys():
storageDict[keyParts[0]] = {}
newObj = storageDict[keyParts[0]]
for i in range(len(keyParts)):
if i == len(keyParts)-1:
# Reached the end of the key, save value to dictionary
newObj["val"] = rowParts[1]
else :
# Not yet at the end of the key
if "subKeys" not in newObj:
newObj["subKeys"] = {}
if keyParts[i+1] not in newObj["subKeys"]:
newObj["subKeys"][keyParts[i+1]] = {}
newObj = newObj["subKeys"][keyParts[i+1]]
def main():
input = [
'key1.subkey1.subsubkey1=value1',
'key1.subkey1.subsubkey2=value2',
'key1.subkey2=value3',
'key2=value4'
]
ans = {}
ans1 = {
'subKeys': ans
}
for row in input:
parts = row.split('=', 1)
setKeyAndValue(ans, parts)
print ans1
main()
输出如下:
{'subKeys': {'key2': {'val': 'value4'}, 'key1': {'subKeys': {'subkey2': {'val': 'value3'}, 'subkey1': {'subKeys': {'subsubkey1': {'val': 'value1'}, 'subsubkey2': {'val': 'value2'}}}}}}}
将您的 OutputDict
变量替换为 storageDict.keys()
并编写了示例主函数。亲自尝试 运行,看看它是否适合你。
我认为您的 OutputDict 仅包含 subKeys
键,因此条件将始终为真,您将用空白词典替换之前添加的词典。
我想将 .properties 文件的键和值解析为 python 字典。 我正在解析的 .properties-file 使用以下语法(键和值是示例):
key1.subkey1.subsubkey1=value1
key1.subkey1.subsubkey2=value2
key1.subkey2=value3
key2=value4
因此每个值都对应一个键,键由一个或多个用句点分隔的级别组成。 目标是创建一个 Python 字典,其中每个键都是一个包含其值和子键的字典。字典应该是递归迭代的,所以每一层都应该遵循相同的结构。
前面的示例应生成以下类型的字典:
'subKeys':
'key1':
'subKeys':
'subkey1':
'subKeys':
'subsubkey1':
'val': 'value1'
'subsubkey2':
'val': 'value2'
'subkey2':
'val': 'value3'
'key2':
'val': 'value4'
我在 python 中使用以下算法循环它:
def setKeyAndValue(storageDict, rowParts):
keyParts = rowParts[0].split('.')
if not keyParts[0] in outputDict:
storageDict[keyParts[0]] = {}
newObj = storageDict[keyParts[0]]
for i in range(len(keyParts)):
if i == len(keyParts)-1:
# Reached the end of the key, save value to dictionary
newObj["val"] = rowParts[1]
else :
# Not yet at the end of the key
if "subKeys" not in newObj:
newObj["subKeys"] = {}
if keyParts[i+1] not in newObj["subKeys"]:
newObj["subKeys"][keyParts[i+1]] = {}
newObj = newObj["subKeys"][keyParts[i+1]]
f = open("FILEPATH.properties", "r")
outputDict = {}
outputDict["subKeys"] = {}
outputDictSubKeys = outputDict["subKeys"]
for row in f:
if not row.startswith('#') and not row.startswith('//'):
parts = row.split('=', 1)
if len(parts)== 2:
setKeyAndValue(outputDictSubKeys, parts)
f.close()
生成的字典 (outputDict) 缺少两个键值对 (key1.subkey1.subsubkey1=value1, key1.subkey1.subsubkey2=value2):
'subKeys':
'key1':
'subKeys':
'subkey2':
'val': 'value3'
'key2':
'val': 'value4'
我很确定问题出在下一行:
newObj = newObj["subKeys"][keyParts[i+1]]
我在循环的每次迭代中替换字典中的 newObj。
有没有办法调整这个现有的算法使其工作,如果没有,我应该如何重新开始?效率不是问题,属性文件不是很大。
一些观察:
解决方案
数据
现在假设我们有一个名为 data.properties
的文件,其结构如下
key1.subkey1.subsubkey1=value1
key1.subkey1.subsubkey2=value2
key1.subkey2=value3
key2=value4
代码
那么我们可以使用下面的代码
import functools
from collections import defaultdict
from pprint import pprint
if __name__ == '__main__':
node = lambda: defaultdict(node)
trie = node()
with open("data.properties", 'r') as file:
for line in file.readlines():
key, value = line.strip().split('=')
functools.reduce(dict.__getitem__, key.split('.'), trie)
val = (functools.reduce(lambda d, key: d.get(key), key.split('.')[:-1], trie))
val[key.split('.')[-1]] = value
pprint(trie)
输出
生成以下输出
defaultdict(<function <lambda> at 0x000002054579EF70>,
{'key1': defaultdict(<function <lambda> at 0x000002054579EF70>,
{'subkey1': defaultdict(<function <lambda> at 0x000002054579EF70>,
{'subsubkey1': 'value1',
'subsubkey2': 'value2'}),
'subkey2': 'value3'}),
'key2': 'value4'})
其中defaultdict
可以忽略,只要key在字典中即可。如果要去掉defaultdict
属性,可以用这个函数
def defaultdict_to_dict(data):
""" Convert a nested defaultdict to a normal dictionary. """
if isinstance(data, defaultdict):
data = dict(data)
if isinstance(data, dict):
for k, v in data.items():
data[k] = defaultdict_to_dict(v)
return data
并使用 pprint(defaultdict_to_dict(trie))
调用它。
这将输出
{'key1': {'subkey1': {'subsubkey1': 'value1',
'subsubkey2': 'value2'},
'subkey2': 'value3'},
'key2': 'value4'}
说明
大部分魔术都发生在以下两行中
1)
functools.reduce(dict.__getitem__, key.split('.'), trie)
2)
val = (functools.reduce(lambda d, key: d.get(key), key.split('.')[:-1], trie))
在第一行中,我们为所有键创建 Trie,所有值(叶子)都将是 defaultdict
。这意味着它可以扩展到任意深度(拥有任意多的子键)。
在第二行中,我们正在遍历 Trie,按照键,直到最后一个键。以 key1.subkey1.subsubkey1=value1
为例,此代码将类似于
val = trie['key1']['subkey1']
下一行 (val[key.split('.')[-1]] = value
) 等于
trie['key1']['subkey1']['subsubkey1'] = 'value1'
我复制了你的函数并测试了你的代码并做了一些修改。以下代码运行良好。
def setKeyAndValue(storageDict, rowParts):
print rowParts
keyParts = rowParts[0].split('.')
if not keyParts[0] in storageDict.keys():
storageDict[keyParts[0]] = {}
newObj = storageDict[keyParts[0]]
for i in range(len(keyParts)):
if i == len(keyParts)-1:
# Reached the end of the key, save value to dictionary
newObj["val"] = rowParts[1]
else :
# Not yet at the end of the key
if "subKeys" not in newObj:
newObj["subKeys"] = {}
if keyParts[i+1] not in newObj["subKeys"]:
newObj["subKeys"][keyParts[i+1]] = {}
newObj = newObj["subKeys"][keyParts[i+1]]
def main():
input = [
'key1.subkey1.subsubkey1=value1',
'key1.subkey1.subsubkey2=value2',
'key1.subkey2=value3',
'key2=value4'
]
ans = {}
ans1 = {
'subKeys': ans
}
for row in input:
parts = row.split('=', 1)
setKeyAndValue(ans, parts)
print ans1
main()
输出如下:
{'subKeys': {'key2': {'val': 'value4'}, 'key1': {'subKeys': {'subkey2': {'val': 'value3'}, 'subkey1': {'subKeys': {'subsubkey1': {'val': 'value1'}, 'subsubkey2': {'val': 'value2'}}}}}}}
将您的 OutputDict
变量替换为 storageDict.keys()
并编写了示例主函数。亲自尝试 运行,看看它是否适合你。
我认为您的 OutputDict 仅包含 subKeys
键,因此条件将始终为真,您将用空白词典替换之前添加的词典。