代码之家  ›  专栏  ›  技术社区  ›  Mike Sandford

无法从对特定语法的pyparsing获得预期结果

  •  2
  • Mike Sandford  · 技术社区  · 10 年前

    我有一个试图解析的二进制有线协议配置文件。这用于允许低带宽链路两侧的计算机以允许用户在现场配置的方式商定哪些位代表哪些数据。

    配置文件字符串如下所示:

    abc:16=>标识符abc有16位

    abc:16 def:12=>标识符abc有16位,标识符def有12位

    abc:16:p=>标识符abc有16位和一个奇偶校验位

    abc:16:ecc=>标识符abc有16位,ecc有两位

    我已经得到了一个语法,我认为应该正确解析它,但我遇到了一个奇怪的问题:我只能使用一个没有奇偶校验或ecc的标识符作为一行中的最后一个语句。语法应该支持在行上的任何位置具有或不具有奇偶校验的标识符,但无论出于什么原因,这都不会发生。

    因此:
    abc:16
    它本身是可以的,因为后面什么都没有

    abc:16:p定义:12
    可以,因为abc:16:p末尾有奇偶校验

    abc:16定义:12
    是不好的,因为abc:16没有奇偶校验,它也不在末尾,但这应该是好的

    abc:16定义:12:p
    也不好,因为非奇偶校验语句不在末尾,但这也应该是完全正确的

    程序如下:

    from pyparsing import *
    import re
    
    abbr = Word(alphas, min=3, max=4)
    #abbr = abbr.setDebug()
    
    separator = Suppress(Literal(":"))
    bits = Word(nums, min=1, max=2)
    parity = Or([CaselessLiteral("P"), CaselessLiteral("ECC")])
    
    bits_part = separator + bits
    #bits_part = bits_part.setDebug()
    
    parity_part = separator + parity
    #parity_part = parity_part.setDebug()
    
    statement = abbr + bits_part + Optional(parity_part)
    #statement = statement.setDebug()
    
    statement_list = StringStart() + statement + ZeroOrMore(Suppress(White()) + statement) + Optional(Suppress(White())) + StringEnd()
    
    tests = ( 
        "abc:16",
        "abc:15:p", 
        "abc:15:p def:14:ecc",
        "abc:17:p def:q ghi:21:", #this one should fail since "q" isn't parity and you shouldn't have a trailing colon with no parity after it
        "abc:16:p def:12", #this passes so it's OK to have a trailing statement without parity
        "abc:15 def:12:p", #this fails but shouldn't
        "abc:16:p def:12 pqr:11", #this is also failing because anything but the last statement missing parity causes failure, but I don't think that's the right behavior
    )
    
    for t in tests:
        try:
            print t
            print statement_list.parseString(t)
        except Exception as e:
            print e
    

    当我在未打开调试的情况下运行它时,我看到以下结果。根据我的理解(以及上面的评论),只有第三个例子应该失败,因为它有“q”,其中的“p”应该是奇偶校验。其他的一切都应该通过,但出于我不理解的原因引发了一个异常。

    abc:16
    ['abc', '16']
    abc:15:p
    ['abc', '15', 'P']
    abc:15:p def:14:ecc
    ['abc', '15', 'P', 'def', '14', 'ECC']
    abc:17:p def:q ghi:21:
    Expected end of text (at char 9), (line:1, col:10)
    abc:16:p def:12
    ['abc', '16', 'P', 'def', '12']
    abc:15 def:12:p
    Expected end of text (at char 7), (line:1, col:8)
    abc:16:p def:12 pqr:11
    Expected end of text (at char 16), (line:1, col:17)
    

    当我打开调试时(在上面的示例代码中全部注释掉了),我只看到“abc:16def:12”,这是输出:

    abc:15 def:12:p
    Match {W:(abcd...) {Suppress:(":") W:(0123...)} [{Suppress:(":") {'P' ^ 'ECC'}}]} at loc 0(1,1)
    Match W:(abcd...) at loc 0(1,1)
    Matched W:(abcd...) -> ['abc']
    Match {Suppress:(":") W:(0123...)} at loc 3(1,4)
    Matched {Suppress:(":") W:(0123...)} -> ['15']
    Match {Suppress:(":") {'P' ^ 'ECC'}} at loc 7(1,8)
    Exception raised:Expected ":" (at char 7), (line:1, col:8)
    Matched {W:(abcd...) {Suppress:(":") W:(0123...)} [{Suppress:(":") {'P' ^ 'ECC'}}]} -> ['abc', '15']
    Expected end of text (at char 7), (line:1, col:8)
    

    在我看来,这证实了它试图匹配parity_part,但显然不存在。但我设置了parity_part,使其为Optional(),所以我无法理解它为什么坚持要查找它。

    此外,那里有一个空白字符(位于abc:16和def:12之间),我觉得应该触发它继续前进,就像我在语法的statement_list部分中指定的那样。为此,我还在结尾处给练习者添加了一个“leaveWhitespace()”调用:

    print statement_list.parseString(t).leaveWhitespace()
    

    但这并没有改变任何东西(因为它没有按照我期望的方式开始解析),所以我不认为问题是它缺少空格。当然,我不能完全忽视它。

    我在这里感到非常困惑,因为我已经从我能想到的每个角度解决了这个问题,但我仍然没有得到我所期望的。我指定的语法错误吗?pyparsing有问题吗?我很有信心我在某个地方犯了错误,但我真的看不出来。

    编辑:

    所以保罗已经指出,我到处都有一些愚蠢的空白,当他把这些都丢弃并简化后,效果很好。空白的东西是故意的,因为我打算阻止人们做类似的事情:

    “abc:10:ecc”

    因为它看起来很糟糕,而不是因为它没有包含正确的信息。

    我不确定阻止人们在我认为不应该的地方放置空间对我来说是否值得,所以保罗的回答可能足以让我继续我的生活。

    但我仍然很好奇,为什么我做的版本不起作用,而他做的修改却起了作用。他们看起来在功能上与我相当。

    1 回复  |  直到 10 年前
        1
  •  2
  •   PaulMcG    10 年前

    知道pyparsing会自己跳过空白,是吗?

    我通过简单地定义statement_list来实现这一点:

    statement_list = OneOrMore(statement)
    

    要防止多个语句同时运行,应使用Group:

    statement_list = OneOrMore(Group(statement))
    

    不要添加自己的StringEnd来强制解析器尝试处理完整的字符串,而是使用 parseAll=True :

    print statement_list.parseString(t, parseAll=True)