【问题标题】:How can I create a nested dictionary object from tree-like file-directory text-file?如何从树状文件目录文本文件创建嵌套字典对象?
【发布时间】:2019-03-31 03:42:35
【问题描述】:

我有一个由标签和行分隔的树结构,如下所示:

a
\t1
\t2
\t3
\t\tb
\t\tc
\t4
\t5

And I am looking to turn this into:

{
'name': 'a',
'children': [
 {'name': '1'},
 {'name': '2'},
 {
   'name': '3'
   'children': [
      {'name': 'b'},
      {'name': 'c'}
    ]
  },
  {'name': '4'},
  {'name': '5'}
  ]
}

用于 d3.js 可折叠树数据输入。我假设我必须以某种方式使用递归,但我不知道如何。

我尝试将输入变成这样的列表:

[('a',0), ('1',1), ('2',1), ('3',1), ('b',2), ('c',2), ('4',1), ('5',1)]

使用此代码:

def parser():
    #run from root `retail-tree`: `python3 src/main.py`
    l, all_line_details = list(), list()
    with open('assets/retail') as f:
        for line in f:
            line = line.rstrip('\n ')
            splitline = line.split('    ') 
            tup = (splitline[-1], len(splitline)-1)
            l.append(splitline)
            all_line_details.append(tup)
            print(tup)
    return all_line_details

这里,第一个元素是字符串本身,第二个元素是该行中的制表符数。不确定执行此操作的递归步骤。感谢任何帮助!

【问题讨论】:

    标签: python python-3.x string parsing string-parsing


    【解决方案1】:

    您可以使用将re.findall 与正则表达式一起使用的函数,该正则表达式匹配作为节点名称的行,后跟以选项卡开头的 0 行或多行,分组为子项,然后递归构建相同的从子字符串中剥离每行的第一个选项卡后的子结构:

    import re
    def parser(s):
        output = []
        for name, children in re.findall(r'(.*)\n((?:\t.*\n)*)', s):
            node = {'name': name}
            if children:
                node.update({'children': parser(''.join(line[1:] for line in children.splitlines(True)))})
            output.append(node)
        return output
    

    所以给定:

    s = '''a
    \t1
    \t2
    \t3
    \t\tb
    \t\tc
    \t4
    \t5
    '''
    

    parser(s)[0] 返回:

    {'name': 'a',
     'children': [{'name': '1'},
                  {'name': '2'},
                  {'name': '3', 'children': [{'name': 'b'}, {'name': 'c'}]},
                  {'name': '4'},
                  {'name': '5'}]}
    

    【讨论】:

    • 感谢您的回答!有没有办法用 4 个空格替换 \t?我的数据在空格中,我尝试使用类似这样的东西,其中 tab_4 = ' ': rx = "^(.*?)\n((?:{0}.*?\n)*)".format(tab_4 ) for name, children in re.findall(rx, s, re.M | re.S)
    • 是的,在将正则表达式中的 \t 替换为 4 个空格后,您只需将子级的行切片从 line[1:] 更改为 line[4:]
    【解决方案2】:

    使用您通过自己的parser 函数提供的列表结构:

    def make_tree(lines, tab_count=0):
        tree = []
        index = 0
        while index < len(lines):
            if lines[index][1] == tab_count:
                node = {"name": lines[index][0]}
                children, lines_read = make_tree(lines[index + 1:], tab_count + 1)
                if children:
                    node["children"] = children
                    index += lines_read
                tree.append(node)
            else:
                break
            index += 1
        return tree, index
    

    测试用例:

    lines = [("a", 0), ("1", 1), ("2", 1), ("3", 1), ("b", 2), ("c", 2), ("4", 1), ("5", 1)]
    
    test_1 = make_tree([("a", 0)])
    assert test_1[0] == [{"name": "a"}], test_1
    test_2 = make_tree([("a", 0), ("b", 1)])
    assert test_2[0] == [{"name": "a", "children": [{"name": "b"}]}], test_2
    test_3 = make_tree(lines)
    expected_3 = [
        {
            "name": "a",
            "children": [
                {"name": "1"},
                {"name": "2"},
                {"name": "3", "children": [{"name": "b"}, {"name": "c"}]},
                {"name": "4"},
                {"name": "5"},
            ],
        }
    ]
    assert test_3[0] == expected_3, test_3
    

    请注意,如果您的源文件有多个根节点(即多行没有前导制表符),输出会被包装在一个列表中,同时也是为了使递归更加简洁。

    【讨论】:

    • 感谢您的回答!我试图运行它,但我认为它失败了这种情况pastebin.com/raw/iT6CPAk3
    • 您的parser 功能是否正常工作?你写它的方式是使用line.split(' '),它看起来应该在标签上拆分,但在你的帖子中似乎是四个空格。如果我在您的parser 函数中替换为line.split('\t') 并将结果传递给make_tree,则测试用例工作正常。
    • 好收获!所以,我使用的是 4 个空格,但为了更好地表示这里的问题,我选择了 '\t'。快速提问:在您的代表中,您对 make_tree 的输入是什么?是:[('a', 0), ('1', 1), ('2', 1), ('3', 1), ('4', 2), ('5', 2), ('6', 2), ('7', 3), ('8', 3), ('9', 3), ('b', 1), ('c', 1), ('d', 1)]?因为在输出中,我没有看到 b, c, or d
    • 啊,那里确实有一个错误。它不会增加孩子的孩子的索引。修复让它有点混乱,但我们可以返回一个带有树结构和读取行的元组,并据此增加索引。现在更新答案。
    猜你喜欢
    • 2021-06-21
    • 2018-05-14
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 2013-07-25
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多