基于 .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。

有没有办法调整这个现有的算法使其工作,如果没有,我应该如何重新开始?效率不是问题,属性文件不是很大。

一些观察:

  • 您尝试做的事情与 Trie 密切相关。您正在生成具有最终值的公共前缀。
  • 对于嵌套结构,来自functools的内置reduce函数将变得非常有用。

解决方案

数据

现在假设我们有一个名为 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 键,因此条件将始终为真,您将用空白词典替换之前添加的词典。