2012-06-15 44 views
0

我试图解析类似于XML文件(没有关联DTD)与pyparsing与可选的,每一个奇怪的问题,并排序:可选元素的列表。每个记录部分看起来有以下内容:Pyparsing:解析器元素

  • <L><L/>标签的东西,
  • <pc><pc/>标签
  • 一个或更多的东西,
  • 可选,内<MW><MW/>标签的东西,
  • 可选地,文字<mul/>和可选的文字<mat/>

这些元素的排序有所不同。

所以我写了下面的(我是新来的pyparsing,请指出,如果我做一些愚蠢的事):

#!/usr/bin/env python 

from pyparsing import * 

def DumbTagParser(tag): 
    tag_close = '</%s>' % tag 
    return Group(
      Literal('<') + Literal(tag).setResultsName('tag') + Literal('>') 
      + SkipTo(tag_close).setResultsName('contents') 
      + Literal(tag_close) 
      ).setResultsName(tag) 

record1 = Group(ZeroOrMore(DumbTagParser('pc'))).setResultsName('pcs') &\ 
      DumbTagParser('L') & \ 
      Optional(Literal('<mul/>')) & \ 
      Optional(DumbTagParser('MW')) & \ 
      Optional(Literal('<mat/>')) 

record2 = Group(ZeroOrMore(DumbTagParser('pc'))).setResultsName('pcs') &\ 
      Optional(DumbTagParser('MW')) & \ 
      Optional(Literal('<mul/>')) & \ 
      DumbTagParser('L') 

def attempt(s): 
    print 'Attempting:', s 
    match = record1.parseString(s, parseAll = True) 
    print 'Match: ', match 
    print 

attempt('<L>1.1</L>') 
attempt('<pc>Page1,1</pc> <pc>Page1,2</pc> <MW>000001</MW> <L>1.1</L>') 
attempt('<mul/><MW>000003</MW><pc>1,1</pc><L>3.1</L>') 
attempt('<mul/> <MW>000003</MW> <pc>1,1</pc> <L>3.1</L> ') # Note end space 

两个解析器record1record2失败,与不同的异常。随着record1,它失败的最后一个字符串(从倒数第二个字符串仅在空间不同):

pyparsing.ParseException: (at char 47), (line:1, col:48) 

record2,它的倒数第二个字符串本身失败:

pyparsing.ParseException: Missing one or more required elements (Group:({"<" "L" ">" SkipTo:("</L>") "</L>"})) (at char 0), (line:1, col:1) 

现在是什么奇怪的是,如果我在record2的定义中互换第2行和第3行,那么它解析得很好!

record2 = Group(ZeroOrMore(DumbTagParser('pc'))).setResultsName('pcs') &\ 
      Optional(Literal('<mul/>')) & \ 
      Optional(DumbTagParser('MW')) & \ 
      DumbTagParser('L') # parses my example strings fine 

(是的,我知道,record2不包含任何规则<mat/>。我试图得到反映到重新排序这种敏感性最小的例子。)

我不知道,如果这是pyparsing或我的代码中的错误,但我真正的问题是我应该如何解析我想要的字符串类型。

+0

为什么downvote?我努力想出一个很好的问题,并将我的代码分解为一个简单的例子,以及所有内容。 :-) – ShreevatsaR

回答

1

我不知道如果你仍然想一个答案,但这里是我的bash ...

我可以看到你的代码下面的问题如下:

  • 你asigned resultsName多次到多个项目,因为Dict最终可能会返回,所以您必须为每个resultsName添加'*'或从多个元素中删除它。我会假设你是在内容之后,而不是标签,并放弃他们的名字。 FYI,设置parser.resultsName(name)的快捷方式是解析器(名称)。
  • 的resultsname设置为“内容”的一切也正如我们就输定了已经提供给我们的信息是一个坏主意。而是通过它的相应标签命名CONTENTS。
  • 您也正在多个项目the0零或内可选,他们已经通过零或“可选”,让我们让他们可以使用“^”运营商的变化,因为没有预定的顺序即。电脑标签可以先于多个标签,反之亦然。在我们经过的时候允许任何梳理和收集这些似乎是合理的。
  • 由于我们还必须处理给定标签的倍数,因此我们将“*”附加到CONTENTS的resultsName,以便我们可以将结果收集到列表中。

首先,我们创建,创建设置开始和结束标记的功能,你DumbTagCreator现在被称为标记集:

from pyparsing import * 

def tagset(str, keywords = False): 
if keywords : 
    return [Group(Literal('<') + Keyword(str) + Literal('>')).suppress(), 
      Group(Literal('</') + Keyword(str) + Literal('/>')).suppress()] 
else : 
    return [Group(Literal('<') + Literal(str) + Literal('>')).suppress(), 
      Group(Literal('</') + Literal(str) + Literal('>')).suppress()] 

接下来,我们创建将解析<tag\>CONTENT</tag>,其中内容是内容解析器我们在有兴趣,返回一个字典,以便我们有{'pc' : CONTENT, 'MW' : CONTENT, ...}

tagDict = {name : (tagset(name)) for name in ['pc','MW','L','mul','mat']} 

parser = None 
for name, tags in tagDict.iteritems() : 
if parser : 
    parser = parser^(tags[0] + SkipTo(tags[1])(name) + tags[1]) 
else : 
    parser = (tags[0] + SkipTo(tags[1])(name) + tags[1]) 

# If you have added the </mul> tag deliberately... 
parser = Optional(Literal('<mul/>')) + ZeroOrMore(parser) 

# If you have added the </mul> tag by acccident... 
parser = ZeroOrMore(parser) 

最后我们测试:

test = ['<L>1.1</L>', 
'<pc>Page1,1</pc> <pc>Page1,2</pc> <MW>000001</MW> <L>1.1</L>', 
'<mul/><MW>000003</MW><pc>1,1</pc><L>3.1</L>', 
'<mul/> <MW>000003</MW> <pc>1,1</pc> <L>3.1</L> '] 

for item in test : 
print {key:val.asList() for key,val in parser.parseString(item).asDict().iteritems()} 

应该产生,假设你想列出的字典:

{'L': ['1.1']} 
{'pc': ['Page1,1', 'Page1,2'], 'MW': ['000001'], 'L': ['1.1']} 
{'pc': ['1,1'], 'MW': ['000003'], 'L': ['3.1']} 
{'pc': ['1,1'], 'MW': ['000003'], 'L': ['3.1']}