0

我正在尝试使用 pyparsing 解析类似 XML 的文件(没有关联的 DTD)。每个记录看起来的一部分有以下内容:

  • 里面的东西<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)

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

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

(是的,我意识到它record2不包含任何规则<mat/>。我试图获得一个反映这种对重新排序的敏感性的最小示例。)

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

4

1 回答 1

1

我不知道你是否还想要一个答案,但这是我的 bash ......

我可以在您的代码中看到以下问题:

  • 您已将 resultsName 多次分配给多个项目,因为最终可能会返回一个 Dict,您必须将“*”添加到每个出现的 resultsName 或从多个元素中删除它。我假设您关注的是内容而不是标签并删除它们的名称。仅供参考,设置 parser.resultsName(name) 的快捷方式是 parser(name)。
  • 将所有内容的结果名称设置为“内容”也是一个坏主意,因为我们会丢失已经可用的信息。而是通过相应的 TAG 命名 CONTENTS。
  • 您还在 0 ZeroOrMore 中使多个项目成为可选,它们已经通过 ZeroOrMore 成为“可选”,因此让我们使用“^”运算符允许它们成为变体,因为没有预定义的序列,即。pc 标签可以在 mul 标签之前,反之亦然。允许任何组合并在我们经过时收集这些似乎是合理的。
  • 由于我们还必须处理给定标签的倍数,我们将“*”附加到 CONTENTS 的 resultsName 中,以便我们可以将结果收集到列表中。

首先我们创建一个函数来创建一组开始和结束标签,你的 DumbTagCreator 现在被称为 tagset :

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>,其中 CONTENT 是我们感兴趣的内容,以返回一个字典,以便我们拥有{'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']}
于 2013-01-04T22:11:18.240 回答