使用python处理重复结构化的文本文件

时间:2014-10-03 18:39:34

标签: python parsing text parser-generator

我有一个大块的文本文件,如:

Student = {
        PInfo = {
                ID   = 0001;
            Name.First = "Joe";
            Name.Last = "Burger";
            DOB  = "01/01/2000";
        };
        School = "West High";
        Address = {
            Str1 = "001 Main St.";
            Zip = 12345;
        };
    };
    Student = {
        PInfo = {
            ID   = 0002;
            Name.First = "John";
            Name.Last = "Smith";
            DOB  = "02/02/2002";
        };
        School = "East High";
        Address = {
            Str1 = "001 40nd St.";
            Zip = 12346;
        };
        Club = "Football";
    };
    ....

学生街区共享相同的条目,如" PInfo","学校"和#34;地址",但其中一些可能有其他条目,例如" Club" " John Smith"的信息这不包括在#34; Joe Burger"中。 我想要做的是获取每个学生的姓名,学校名称和邮政编码,并将其存储在字典中,如

    {'Joe Burger':{School:'West High', Zip:12345}, 'John Smith':{School:'East High', Zip:12346}, ...}

作为python编程的新手,我尝试打开文件并逐行分析,但它看起来很麻烦。真实文件比我上面发布的示例相当大且复杂。我想知道是否有更简单的方法来做到这一点。谢谢。

3 个答案:

答案 0 :(得分:4)

要解析文件,您可以定义描述输入格式的语法,并使用它来生成解析器。

many language parsers in Python。例如,您可以使用Grako作为输入,将EBNF变体中的语法作为输入,并在Python中输出memoizing PEG解析器。

要安装Grako,请运行pip install grako

使用Grako的EBNF语法格式的格式语法:

(* a file is zero or more records *)
file = { record }* $;
record = name '=' value ';' ;
name = /[A-Z][a-zA-Z0-9.]*/ ;
value = object | integer | string ;
(* an object contains one or more records *)
object = '{' { record }+ '}' ;
integer = /[0-9]+/ ;
string = '"' /[^"]*/ '"';

要生成解析器,请将语法保存到文件,例如Structured.ebnf并运行:

$ grako -o structured_parser.py Structured.ebnf

它创建structured_parser模块,可用于从输入中提取学生信息:

#!/usr/bin/env python
from structured_parser import StructuredParser

class Semantics(object):
    def record(self, ast):
        # record = name '=' value ';' ;
        # value = object | integer | string ;
        return ast[0], ast[2] # name, value
    def object(self, ast):
        # object = '{' { record }+ '}' ;
        return dict(ast[1])
    def integer(self, ast):
        # integer = /[0-9]+/ ;
        return int(ast)
    def string(self, ast):
        # string = '"' /[^"]*/ '"';
        return ast[1]

with open('input.txt') as file:
    text = file.read()
parser = StructuredParser()
ast = parser.parse(text, rule_name='file', semantics=Semantics())
students = [value for name, value in ast if name == 'Student']
d = {'{0[Name.First]} {0[Name.Last]}'.format(s['PInfo']):
     dict(School=s['School'], Zip=s['Address']['Zip'])
     for s in students}
from pprint import pprint
pprint(d)

输出

{'Joe Burger': {'School': u'West High', 'Zip': 12345},
 'John Smith': {'School': u'East High', 'Zip': 12346}}

答案 1 :(得分:1)

它不是json,而是类似的结构。你应该能够将它重新格式化为json。

  1. “=” - > “:”
  2. 使用'“'
  3. 引用所有键
  4. “;” - > “”
  5. 删除所有“,”后跟“}”
  6. 把它放在花括号中
  7. 用json.loads解析它

答案 2 :(得分:1)

对于这种情况,我使用Marpa::R2Perl Marpa, a general BNF parser接口来parser。它允许将文本描述为语法规则并将它们解析为数组树(解析树)。然后,您可以遍历树以将结果保存为散列哈希(散列是python&#39的字典的perl)或按原样使用。

我使用您的输入做了一个工作示例:result treeParse values from a block of text based on specific keys

希望这有帮助。

P.S。 ast_traverse():{{3}}

的示例