Python - 将结构化文本转换并过滤为对象

Python - Convert and filter structured text into object

提问人:user14965984 提问时间:7/3/2023 更新时间:7/3/2023 访问量:48

问:

当前问题。我正在处理一组数据文件,这些文件基本上看起来像这样:

{39107,
    {31685,
        {   f24c4ec6-1e59-47a0-9736-8c823eda0d28,
            "N",
            7
        },
        {   c71dce36-4295-49e4-be03-7c60969b96c3,
            "A",
            8
        },
        {   f80fce14-f001-4b20-84d5-7a00f0788f6b,
            "A",
            9
        },
    }
}

{0,
    {4659,
        {
                        7c90ea6a-12f5-4c54-bfe0-e38120a6e364,
                        "fieldname27472",
                        "N",
                        27472,
                        "",
                        {3,
                                {"field1",
                                        0,
                                        {1,
                                                {
                                                        "B",
                                                        16,
                                                        0,
                                                        "",
                                                        0
                                                }
                                        },
                                        "",
                                        0
                                },
                                {"field2",
                                        0,
                                        {1,
                                                {
                                                "T",
                                                0,
                                                0,
                                                "",
                                                0}
                                        },
                                        "",
                                        0
                                },
                                {"field3",
                                        0,
                                        {1,
                                                {
                                                        "L",
                                                        0,
                                                        0,
                                                        "",
                                                        0
                                                }
                                        },
                                        "",
                                        0
                                },
                        },
                        {0},
                        {1,
                                {
                                        edcba,
                                        "ByID",
                                        abcde,
                                        1,
                                        {1,
                                                "ID"
                                        },
                                        1,
                                        0,
                                        0
                                }
                        },
                        1,
                        "S",
                        {0},
                        {0},
                        "",
                        0,
                        0
                }
        }
}

数据集前的数字,例如 4659 表示以下数据容器的数量。 某些值未括在引号中,例如本例中的 uuid,或随机字符串。

我的目标是在 python 对象(如列表或元组)中转换这些数据结构,然后将它们转换为 JSON 进行外部处理。

现在我有一个 2 阶段的过程。 Stage1 进行初始转换和数据评估。 Stage2 筛选数据,删除多余的值(例如实际元素之前的元素数)和嵌套列表。

import json

file = 'stack1.json'

def stage1(msg):
    buffer = ''
    st,fh,delim,encase = '[',']',',', '"'
    msg = msg.translate(str.maketrans('{}',st+fh)).replace('\n', '').replace('\r', '').replace('\t', '')
    while True:
        fhpos = msg.find(fh)
        if fhpos >= 0:
            head = msg[:fhpos+1]
            if head:
                stpos = head.rfind(st)
                if stpos>=0:
                    teststring = head[stpos+1:fhpos].split(delim)
                    for idx,sent in enumerate(teststring):
                        if not (sent.startswith(encase) or sent.endswith(encase)) or sent.count('-') == 4:
                            teststring[idx] = (f'"{teststring[idx]}"')
                            break
                    buffer+= head[:stpos+1]+','.join(teststring)+fh
                else: buffer+=fh
            msg = msg[fhpos+1:]
        else:
            break
    return buffer

def stage2(lst):
    if not any([isinstance(i,list) for i in lst]):
        return tuple(lst)
    if not isinstance(lst[0],list) and all([isinstance(j,list) for j in lst[1:]]):
        lst = stage2(lst[1:])
        if all([isinstance(j,(list,tuple)) for j in lst]) and len(lst) == 1:
            lst, = lst
    for idx,i in enumerate(lst):
        if isinstance(i,list):
            lst[idx] = stage2(i)
        else:
            continue
    return stage2(lst)

with open(file, 'r') as f:
    data = f.read()
    try:
        s1 = stage1(data)
        print("STAGE1\n",s1)
        s2 = stage2(json.loads(s1))
        print("STAGE2\n",json.dumps(s2, indent=2))
    except Exception as e: print(e)

目前结果

示例1:

STAGE1
[39107,[31685,["f24c4ec6-1e59-47a0-9736-8c823eda0d28","N",7],["c71dce36-4295-49e4-be03-7c60969b96c3","A",8],["f80fce14-f001-4b20-84d5-7a00f0788f6b","A",9]]]
STAGE2
 [
  [
    "f24c4ec6-1e59-47a0-9736-8c823eda0d28",
    "N",
    7
  ],
  [
    "c71dce36-4295-49e4-be03-7c60969b96c3",
    "A",
    8
  ],
  [
    "f80fce14-f001-4b20-84d5-7a00f0788f6b",
    "A",
    9
  ]
]

示例2:

STAGE1
[0,[4659,[7c90ea6a-12f5-4c54-bfe0-e38120a6e364,"fieldname27472","N",27472,"",[3,[aa-aa-a-a-a,"field1",0,[1,["B","16",0,"",0]]],["field2",0,[1,["T","0",0,"",0]]],["field3",0,[1,["L","0",0,"",0]]]],["0"],[1,[edcba,"ByID",abcde,1,["1","ID"]]],1,"S",["0"],["0"]]]]
STAGE2
Expecting ',' delimiter: line 1 column 12 (char 11)

示例 2 失败,因为并非所有值都带有引号。

哪些库可能适合这种情况? 数据集相当大,目前第一个示例是 ~5M 个字符,stage1 最多需要 1 分钟来处理。

未来问题:像这样转换和过滤数据的最佳方法是什么? 我认为在同一次传递中转换 AND 过滤更快,而不是多次执行完全扫描。 我读过关于 PLYPEG 的文章,但我认为这不是适合这项工作的工具。

json python-3.x 解析 数据结构

评论

0赞 Mark 7/3/2023
你试过正则表达式吗?
0赞 user14965984 7/3/2023
好吧,我想过,但没有想出正确的实现方案。而且我认为数据对于使用正则表达式来说太大了。我可能错了
0赞 Mark 7/3/2023
出于好奇,它有多大(兆字节)?
0赞 user14965984 7/3/2023
不是很大,大约 4 到 5 个,但文件总数 ~49000

答:

1赞 trincot 7/3/2023 #1

我的目标是在 python 对象(如列表或元组)中转换这些数据结构,然后将它们转换为 JSON 进行外部处理。

实际上,我会首先将字符串转换为有效的JSON。然后使用 将其转换为 Python 数据结构,之后您可以使用标准迭代来根据需要进行过滤和映射。json.loads

如果这些示例具有充分的代表性,那么为了使其符合 JSON 标准,基本上有 3 个“问题”需要解决:

  • 用作数组边界的大括号应替换为方括号
  • 应删除尾随逗号(在最后一个数组元素之后)
  • 十六进制值(可能包括连字符)应该用引号引起来(或者,它们可以用前缀编码,但较长的数字序列必须分解成多个部分,所以我不会那样做)。0x

我还假设:

  • 充当数组边界的左大括号将始终出现在行首(忽略间距)
  • 充当数组边界的右大括号(可能紧随其后的尾随逗号)将始终出现在行的末尾。
  • 不带引号的十六进制值将显示在行首(忽略空格),但一个左大括号除外,它可以出现在值之前。

如果所有这些假设都是正确的,那么以下应该有效:

import re
import json

def process(s):
    # replace braces with square brackets
    s = re.sub(r"^(\s*){\n?", r"\1[\n", s, flags=re.M)
    s = re.sub(r"}(,?)$", r"]\1", s, flags=re.M)
    # remove trailing commas (not valid in JSON)
    s = re.sub(r",$(\s+])", r"\1", s, flags=re.M)
    # wrap hex in quotes
    s = re.sub(r'^(\s*)(?=.*[\-a-z])([\w\-]+)', r'\1"\2"', s, flags=re.M)
    return json.loads(s)

with open("stack.json", 'r') as f:
    data = process(f.read())
    print(data)

评论

1赞 user14965984 7/3/2023
谢谢,这段代码适用于这个例子,我可以让它适用于实际数据。我找到了pyparsing模块,有什么关于将其用于此任务的提示吗?
0赞 trincot 7/3/2023
很高兴能帮上忙!不过,我没有pyparsing的经验。