2016-09-22 48 views
2

私は、文書のコメントからpyparsingを使用してkey:valueのペアを解析しようとしています。キーは行の先頭から始まり、値が続きます。空白で始まる複数の行に値を続けることができます。pyparsing LineStartの使い方は?

import pyparsing as pp 

instring = """ 
-- This is (a) #%^& comment 

/* 
name1: val 
name2: val2 with $*&#@) junk 
name3: val3: with @)(*% multi- 
     line: content 
*/ 
""" 

comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug() 
identifier = pp.Word(pp.alphanums + "_").setDebug() 
meta1 = pp.LineStart() + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()) 
meta2 = pp.LineStart() + pp.White() + pp.SkipTo(pp.LineEnd()) 
metaval = meta1 + pp.ZeroOrMore(meta2) 
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/") 

if __name__ == "__main__": 
    p = metalist.parseString(instring) 
    print(p) 

はで失敗:

Matched {Empty SkipTo:(LineEnd) Empty} -> ['This is (a) #%^& comment'] 

File "C:\Users\user\py3\lib\site-packages\pyparsing.py", line 2305, in parseImpl 
raise ParseException(instring, loc, self.errmsg, self) 
pyparsing.ParseException: Expected start of line (at char 32), (line:4, col:1) 

pyparsing whitespace match issuesに対する答えが

LineStart has always been difficult to work with, but ... 

パーサーが行4列1である場合に言う(最初のキー:値対)、次いで理由でありますそれは行の開始を見つけることができません?空白で始まらない行と空白で始まる行を認識する正しいpyparsing構文は何ですか?

答えて

2

LineStartとの混乱は、LineEndの場合、'\n'文字を検索できますが、LineStartの文字はありません。したがって、LineStartでは、現在のパーサーの位置が'\n'の直後にあるかどうかを調べます。または現在の場合'\n'の場合は、それを移動して引き続き実行します。残念ながら、私はレポートの場所を混乱させる場所でこれを実装しました。そのため、「X col 1の行の開始に失敗しました」というような奇妙なエラーが表示されます。行の開始。また、この暗黙的な改行スキップを再訪する必要があると思います。つまり、すべての空白スキップは一般的にはLineStartです。 LSとMETA1とmeta2でLineStart参照を

LS = pp.Optional(pp.LineEnd()) + pp.LineStart() 

と置き換え:

は今のところ、私はよう、少し自分のライン開始式を拡張することによって動作するようにあなたのコードを得ている

comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug() 
identifier = pp.Word(pp.alphanums + "_").setDebug() 
meta1 = LS + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()) 
meta2 = LS + pp.White() + pp.SkipTo(pp.LineEnd()) 
metaval = meta1 + pp.ZeroOrMore(meta2) 
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/") 

LineStartの状況で不快な場合は、もう1つの方法を試してみてください。列1で始まる識別子を受け入れるための解析時間条件を使用する:

comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug() 

identifier = pp.Word(pp.alphanums + "_").setName("identifier") 
identifier.addCondition(lambda instring,loc,toks: pp.col(loc,instring) == 1) 

meta1 = identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()).setDebug() 
meta2 = pp.White().setDebug() + pp.SkipTo(pp.LineEnd()).setDebug() 
metaval = meta1 + pp.ZeroOrMore(meta2, stopOn=pp.Literal('*/')) 
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.LineEnd() + pp.OneOrMore(metaval) + pp.Literal("*/") 

このコードは、LineStartと完全には分かりませんが、私はこの特定のトークンに何をしたいのかを理解しています。 */が継続的なコメントの内容として誤って処理されないように、の繰り返しをmetavalに変更する必要がありました。

これに感謝しています - 私はすぐにパッチを当ててLineStartの変更を出して欲しいとは思わないし、他の互換性や他のエッジケースを見落としていることがわかりました。このクラスでしかし、2.1.10を出す前に、この振る舞いを明確にするための努力をします。

+0

ありがとうございます - pp.col条件は、トリックです。しかし、以下の答えに示されているように、stopOn引数は正しく動作していません。pp.Literalでは動作しますが、pp.Wordでは動作しません。あなたが次のリリースに取り組んでいるときに考慮すべきこと。 – Dave

+0

LineStartの改良版が2.1.10でリリースされました。 – PaulMcG

+0

これは私のために働く: 'seq = OneOrMore(Word(nums)、stopOn = Word(" 0 ")); print(seq.parseString( "349875 2330 204 123 000")) 'は' ['349875'、 '2330'、 '204'、 '123'] 'を返します。 – PaulMcG

関連する問題