我有一个正在尝试解析的二进制有线协议(protocol)配置文件。这用于允许低带宽链路两侧的计算机以允许用户在现场配置它们的方式就哪些位代表哪些数据达成一致。
配置文件字符串如下所示:
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 def:12
可以,因为 abc:16:p 末尾有奇偶校验
abc:16 def:12
不行,因为 abc:16 没有奇偶校验,而且它不在末尾,但这应该没问题
abc:16 def: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:16 def: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 显然不存在。但我已经将其设置为 parity_part 是Optional(),所以我无法弄清楚为什么它坚持要找到它。
此外,那里有一个空白字符(在 abc:16 和 def:12 之间),我觉得应该触发它继续前进,就像我在语法的 statements_list 部分中指定的那样。为此,我还在最后添加了对练习器的“leaveWhitespace()”调用:
print statement_list.parseString(t).leaveWhitespace()
但这并没有改变任何东西(因为它没有开始按照我期望的方式解析),所以我不认为问题在于它缺少空格。当然,我不能完全忽视它。
我在这里感到非常困惑,因为我已经从我能想到的各个角度解决了这个问题,但我仍然没有得到我所期望的结果。我指定的语法错误吗? pyparsing 做错了什么吗?我非常有信心我在某个地方犯了错误,但我真的看不到它。
编辑:
所以保罗指出我到处都有一堆愚蠢的空白东西,当他扔掉所有这些东西并简化时,一切都很好。空白的东西是故意存在的,因为我要尝试阻止人们做类似的事情:
“abc:10:ecc”
因为它看起来很糟糕,而不是因为它不包含正确的信息。
我不确定阻止人们在我认为不应该放置空格的地方是否值得,所以保罗的回答可能足以让我继续我的生活。
但我还是很好奇,为什么我编的版本不起作用,而他所做的修改却起作用。他们在功能上看起来与我相当。
最佳答案
您确实知道 pyparsing 会自行跳过空格,是吗?
我通过将statement_list定义为普通的来使其工作:
statement_list = OneOrMore(statement)
为了防止多个语句一起运行,您应该使用 Group:
statement_list = OneOrMore(Group(statement))
不要添加自己的 StringEnd 来强制解析器尝试处理完整字符串,而是使用 parseAll=True
:
print statement_list.parseString(t, parseAll=True)
关于python - 对特定语法进行 pyparsing 时未获得预期结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25044570/