以递归方式收集python中的字符串块

时间:2016-07-29 12:08:30

标签: python

我有一个这样格式的自定义数据文件:

{
    data = {
        friends = {
            max = 0 0,
            min = 0 0,
        },
        family = {
            cars = {
                van = "honda",
                car = "ford",
                bike = "trek",
            },
            presets = {
                location = "italy",
                size = 10,
                travelers = False,
            },
            version = 1,
        },
    },
}

我想收集数据块,这意味着每组{}之间的字符串,同时保持一个hierarhcy。此数据不是典型的json格式,因此这不是一种可能的解决方案。

我的想法是创建一个像这样的类对象

class Block:
    def __init__(self, header, children):
        self.header = header
        self.children = children

然后,我将逐行循环数据,以某种方式收集必要的数据,这样我的结果输出就像这样......

Block("data = {}", [
    Block("friends = {max = 0 0,\n min = 0 0,}", []),
    Block("family = {version = 1}", [...])
])

总之,我正在寻找帮助,我可以将其序列化为有用的数据,然后我可以轻松操作。所以我的方法是通过使用{}作为分隔符来闯入对象。 如果有人有关于如何更好地处理这个问题的建议,我会全力以赴。再次感谢你。

到目前为止,我刚刚实现了代码的基本代码段

class Block:
    def __init__(self, content, children):
        self.content = content
        self.children = children

def GetBlock(strArr=[]):
    print len(strArr)
#   blocks = []
    blockStart = "{"
    blockEnd = "}"

with open(filepath, 'r') as file:
    data = file.readlines()
    blocks = GetBlock(strArr=data)

2 个答案:

答案 0 :(得分:1)

您可以创建一个to_block函数,该函数将文件中的行作为迭代器,并从这些函数递归创建嵌套字典。 (当然你也可以使用自定义的Block课程,但我真的没有看到这样做的好处。)

def to_block(lines):
    block = {}
    for line in lines:
        if line.strip().endswith(("}", "},")):
            break
        key, value = map(str.strip, line.split(" = "))
        if value.endswith("{"):
            value = to_block(lines)
        block[key] = value
    return block

调用它时,你必须剥离第一行。此外,评估"叶子"例如数字或字符串留给读者作为练习。

>>> to_block(iter(data.splitlines()[1:]))
{'data': {'family': {'version': '1,', 
                     'cars': {'bike': '"trek",', 'car': '"ford",', 'van': '"honda",'}, 
                     'presets': {'travelers': 'False,', 'size': '10,', 'location': '"italy",'}}, 
          'friends': {'max': '0 0,', 'min': '0 0,'}}}

或者从文件中读取时:

with open("data.txt") as f:
    next(f)  # skip first line
    res = to_block(f)

或者,您可以执行一些预处理以将该字符串转换为JSON(-ish)字符串,然后使用json.loads。但是,我不会一直在这里,而只是将值包装到""(并在此之前用"替换原来的'),否则会有太大的风险意外将带空格的字符串转换为列表或类似字符串。您可以在创建JSON数据后对其进行排序。

>>> data = data.replace('"', "'")
>>> data = re.sub(r'= (.+),$',     r'= "\1",', data, flags=re.M)
>>> data = re.sub(r'^\s*(\w+) = ', r'"\1": ',  data, flags=re.M)
>>> data = re.sub(r',$\s*}',       r'}',       data, flags=re.M)
>>> json.loads(data)
{'data': {'family': {'version': '1', 
                     'presets': {'size': '10', 'travelers': 'False', 'location': "'italy'"}, 
                     'cars': {'bike': "'trek'", 'van': "'honda'", 'car': "'ford'"}}, 
          'friends': {'max': '0 0', 'min': '0 0'}}}

答案 1 :(得分:1)

你也可以在正则表达式替换的帮助下使用ast或json。

import re

a = """{
    data = {
        friends = {
            max = 0 0,
            min = 0 0,
        },
        family = {
            cars = {
                van = "honda",
                car = "ford",
                bike = "trek",
            },
            presets = {
                location = "italy",
                size = 10,
                travelers = False,
            },
            version = 1,
        },
    },
}"""

#with ast
a = re.sub("(\w+)\s*=\s*", '"\\1":', a)
a = re.sub(":\s*((?:\d+)(?: \d+)+)", lambda x:':[' + x.group(1).replace(" ", ",") + "]", a)
import ast
print ast.literal_eval(a)
#{'data': {'friends': {'max': [0, 0], 'min': [0, 0]}, 'family': {'cars': {'car': 'ford', 'bike': 'trek', 'van': 'honda'}, 'presets': {'travelers': False, 'location': 'italy', 'size': 10}, 'version': 1}}}

#with json
import json
a = re.sub(",(\s*\})", "\\1", a)
a = a.replace(":True", ":true").replace(":False", ":false").replace(":None", ":null")
print json.loads(a)
#{u'data': {u'friends': {u'max': [0, 0], u'min': [0, 0]}, u'family': {u'cars': {u'car': u'ford', u'bike': u'trek', u'van': u'honda'}, u'presets': {u'travelers': False, u'location': u'italy', u'size': 10}, u'version': 1}}}