2012-06-15 43 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']}