使用Python(pyparsing)解析结构化文本文件

7
由于某些原因,我正在使用的REST API并不输出JSON或XML,而是使用一种特殊的结构化文本格式。在其最简单的形式下,它长这样:
SECTION_NAME    entry  other qualifying bits of the entry
                entry2 other qualifying bits
                ...

它们看起来并不是制表符分隔的,而是空格分隔的,限定位可能包含带有空格的单词。SECTION_NAME和条目之间的间距也是可变的,范围从1到几个(6个或更多)空格。

此外,格式的一部分包含以下形式的条目

SECTION_NAME entry
  SUB_SECTION more information
  SUB_SECTION2 more information

供参考,以下是部分真实数据的摘录(省略了一些部分),其中显示了该结构的使用:

ENTRY       hsa04064                    Pathway
NAME        NF-kappa B signaling pathway - Homo sapiens (human)
DRUG        D09347  Fostamatinib (USAN)
            D09348  Fostamatinib disodium (USAN)
            D09692  Veliparib (USAN/INN)
            D09730  Olaparib (JAN/INN)
            D09913  Iniparib (USAN/INN)
REFERENCE   PMID:21772278
  AUTHORS   Oeckinghaus A, Hayden MS, Ghosh S
  TITLE     Crosstalk in NF-kappaB signaling pathways.
  JOURNAL   Nat Immunol 12:695-708 (2011)

当我试图将这种奇怪的格式解析成更合理的东西(一个可以转换为JSON的字典),我不确定该怎么做:盲目地按空格分割会导致混乱(它还会影响带有空格的信息),而且我不确定如何确定一个部分何时开始或结束。文本操作是否足够完成任务,还是应该使用更复杂的方法?
编辑:
我开始使用pyparsing进行处理,但多行记录使我困惑,以下是DRUG的示例:
 from pyparsing import *
 punctuation = ",.'`&-"
 special_chars = "\()[]"

 drug = Keyword("DRUG")
 drug_content = Word(alphanums) + originalTextFor(OneOrMore(Word(
      alphanums + special_chars))) + ZeroOrMore(LineEnd())
 drug_lines = OneOrMore(drug_content)
 drug_parser = drug + drug_lines

当应用于示例中DRUG的前3行时,我得到了错误的结果(\n转换为实际返回以便易读性):

 ['DRUG', ['D09347', 'Fostamatinib (USAN)
        D09348  Fostamatinib disodium      (USAN)
        D09692  Veliparib (USAN']]

如您所见,随后的条目会全部合并在一起,而我原本的期望是:
 ['DRUG', [['D09347', 'Fostamatinib (USAN)'], ["D09348", "Fostamatinib disodium (USAN)"],
           ['D09692', ' Veliparib (USAN)']]]

你尝试过按空格分割,但限制分割的次数吗? - inspectorG4dget
@inspectorG4dget:我考虑过了,但是单个条目具有可变的空间要求(因此可能每个部分都需要其特定数量的拆分)。 - Einar
啊哈!那么也许re.split会是一个更好的选择。 - inspectorG4dget
1
你也在pyparsing维基上问了这个问题,可以看看我的回答:http://pyparsing.wikispaces.com/message/view/home/55280466 - PaulMcG
2个回答

3
我建议您采用基于解析器的方法。例如,可以使用Python PLY来完成此任务。

我很快会测试这个和Antonio Beamud的解决方案。谢谢。 - Einar
2
@Einar 另一个解析选项可以是 http://pyparsing.wikispaces.com/,它有合理的文档和丰富的示例。 - Jon Clements
加入了 pyparsing 的 try 语句,针对单行已经可以实现,但是多行还有些问题。 - Einar

0

最好的方法是使用正则表达式,例如:

m = re.compile('^ENTRY\s+(.*)$')
m.search(line)
if m:
   m.groups()[0].strip()

对于没有输入的行,应该使用您检测到的最后一个输入。

更简单的方法是按条目拆分,例如:

vals = line.split('DRUG')
if len(vals) > 1:
     drug_field = vals[1].strip()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接