从文本字符串解析数据对象结构

1 投票
1 回答
1095 浏览
提问于 2025-04-18 12:05

我最近在尝试使用pyparsing这个模块,想更好地理解解析的基本概念。
我在面试中遇到了一个问题(这个问题已经提交,所以我觉得没有伦理上的问题),需要处理一个类似下面这种的数据结构,从文本文件中读取。

Collection Top_Level_Collection "Junk1"
{
  Column Date_time_column 1 {"01-21-2011"}
  Collection Pig_Entry "Sammy"
  {
    Column Animal_locations 35 {"Australia", "England", "U.S."}
    Data 4
    {
      4 0 72033 Teeth 2 {1 "2", 1 "3"};
      1 0 36331 Teeth 2 {2 "2", 3 "4"};
      2 3 52535 Teeth 2 {6 "4", 9 "3"};
      4 0 62838 Teeth 2 {3 "7", 7 "6"};
    }
  }
}

我可以用正则表达式和计数列的方式得到一个比较粗糙的解决方案,来提取数据的部分内容并把它们组合起来,但我想更深入地学习解析,以便做得更优雅一些。
可以看到,基本结构是先有“主抽象数据类型”,然后是可选的“具体数据类型”,接着是“名称”或“条目数量”,而且这个结构可以无限嵌套。

这是我目前尝试解析成字典的结果:

import numpy as np
import pyparsing as pp

test_str = '''
Collection Top_Level_Collection "Junk"
{
  Column Date_time_column 1 {"01-21-2011"}
  Collection Pig_Entry "Sammy"
  {
    Column Animal_locations 35 {"Australia", "England", "U.S."}
    Data 4
    {
      4 0 72033 Teeth 2 {1 "2", 1 "3"};
      1 0 36331 Teeth 2 {2 "2", 3 "4"};
      2 3 52535 Teeth 2 {6 "4", 9 "3"};
      4 0 62838 Teeth 2 {3 "7", 7 "6"};
    }
  }
}
'''
if __name__ == '__main__':
    expr = pp.Forward()

    object_type = pp.Word( pp.alphanums + '_')
    object_ident = pp.Word( pp.alphanums + '_')
    object_name_or_data_num = pp.Word( pp.alphanums + '_".')

    ident_group = pp.Group(object_type + pp.Optional(object_ident) + object_name_or_data_num)
    nestedItems = pp.nestedExpr("{", "}")

    expr << pp.Dict(ident_group + nestedItems)

    all_data_dict = (expr).parseString(test_str).asDict()

    print all_data_dict
    print all_data_dict.keys()

这个结果返回的是:

{'Column': (['Date_time_column', '1', (['"01-21-2011"'], {}), 'Collection', 'Pig_Entry', '"Sammy"', (['Column', 'Animal_locations', '35', (['"Australia"', ',', '"England"', ',', '"U.S."'], {}), 'Data', '4', (['4', '0', '72033', 'Teeth', '2', (['1', '"2"', ',', '1', '"3"'], {}), ';', '1', '0', '36331', 'Teeth', '2', (['2', '"2"', ',', '3', '"4"'], {}), ';', '2', '3', '52535', 'Teeth', '2', (['6', '"4"', ',', '9', '"3"'], {}), ';', '4', '0', '62838', 'Teeth', '2', (['3', '"7"', ',', '7', '"6"'], {}), ';'], {})], {})], {}), 'Collection': (['Top_Level_Collection', '"Junk"'], {})}
['Column', 'Collection']

不过,我希望返回的结果能更容易地传递给Python中的类,以便创建对象。我的最佳猜测是把它们放在一个嵌套字典中,键是一个包含2或3种对象类型的元组,值则是一个字典,里面包含每个键值。也就是说,类似于下面这样的结构:

{ (Collection, Top_Level_Collection, "Junk1"):
    { (Column, Date_time_column): ["01-21-2011"], 
      (Collection, Pig_Entry, "Sammy"): 
        { (Column, Animal_locations): ["Australia", "England", "U.S."],
            (Data): [[ 4 0 72033 {(Teeth):[1 "2", 1 "3"]} ]
                    [ 1 0 36331 {(Teeth):[2 "2", 3 "4"]} ]
                    [ 2 3 52535 {(Teeth):[6 "4", 9 "3"]} ]
                    [ 4 0 62838 {(Teeth):[3 "7", 7 "6"]} ]]
        }
    }
}

1 个回答

3

你需要为你的数据创建类,然后使用“setParseAction”来设置解析器,这样你就可以创建你想要的数据结构。下面是一个简单的示例:

#!/usr/bin/env python

from pyparsing import *

test_str="Alpha 1\nBeta 2\nCharlie 3"

aStmt = Word(alphas)("name") + Word(nums)("age")

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

def myParse(t):
    return Person(t.name, t.age)

aStmt.setParseAction(myParse)

for aline in test_str.split('\n'):
    print aline
    print aStmt.parseString(aline)

撰写回答