0

XML に似たファイル (DTD が関連付けられていない) を pyparsing で解析しようとしています。各レコードの一部には、次の内容が含まれています。

  • タグ内<L>の何か、<L/>
  • <pc>and<pc/>タグ内の 1 つ以上のもの、
  • <MW>必要に応じて、 and<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すると、最後の文字列で失敗します (最後から 2 番目の文字列とはスペースのみが異なります)。

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

を使用するとrecord2、最後から 2 番目の文字列自体で失敗します。

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

まだ答えが欲しいかどうかはわかりませんが、ここに私のバッシュがあります...

あなたのコードには次の問題があることがわかります。

  • resultsName を複数の項目に複数回割り当てました。Dict が最終的に返される可能性があるため、resultsName の出現ごとに「*」を追加するか、多数の要素から削除する必要があります。タグではなくコンテンツを求めていると仮定し、それらの名前を削除します。参考までに、parser.resultsName(name) を設定するためのショートカットは parser(name) です。
  • すべての結果名を「Contents」に設定することも、既に利用可能な情報が失われるため、悪い考えです。むしろ、それに対応する 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 は関心のあるコンテンツであり、次のように dict を返します{'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 に答える