2016-09-22 48 views
2

我试图使用pyparsing从文档中的注释解析键:值对。一个键从一行的开始处开始,然后是一个值。值可以在以空格开头的多行上继续。如何使用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'之后;或者如果它当前是,则 a '\n',移过去并继续。不幸的是,我在一个弄乱了报告位置的地方实现了这个功能,所以你会得到那些奇怪的错误,比如“找不到第一行第一行的起始行”,这听起来确实像是应该成功匹配一条线的开始。另外,我认为我需要重新审视这种隐含的换行符,或者就此而言,所有空白跳过的都是LineStart

现在,我已经得到了你的代码通过扩展线开始略有表达,因为工作:

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

,并取代了META1和meta2的LineStart引用与LS:

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("*/") 

如果LineStartLineStart一起出现这种情况,您可能会尝试另一种策略:使用分析时间条件只接受从第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中的ZeroOrMore重复,以便*/不会被意外处理为继续评论内容。

感谢您对此的耐心等待 - 我并不热衷于快速发布补丁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