我正在尝试使用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
解析器record1
和record2
都会失败,但有不同的例外情况。对于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 :(得分:1)
我不知道你是否还想要答案,但这是我的bash ......
我可以看到代码中的以下问题如下:
首先我们创建一个函数来创建一组开始和结束标记,你的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']}