解析一系列二进制数字

时间:2014-02-18 11:55:21

标签: python parsing grammar pyparsing parsimonious

如何解析python中的二进制数字序列。 以下是我想要做的一个例子。

我有一系列二进制数字,例如

sequence =  '1110110100110111011011110101100101100'

并且,我需要解析它并提取数据。

假设上面的序列包含开始,id,数据和结束字段

start是2比特字段,id是8比特字段,数据字段可以从1到8192比特变化,结束是4比特字段。

并且在解析之后我期望输出如下:

result = {start : 11,
          id : 10110100,
          data : 11011101101111010110010,
          end : 1100,
         }

我在我的一个应用程序中使用它。 我能够使用正则表达式解析序列,但问题是正则表达式必须由用户编写。因此,作为替代方案,我使用BNF语法,因为语法更具可读性。

我尝试使用python的简约 pyparsing 解析器来解决这个问题。但我无法找到长度可变的字段的解决方案。

我在parsimonious中为python编写的语法如下:

grammar = """sequence = start id data end

start = ~"[01]{2}"
id = ~"[01]{8}"
data = ~"[01]{1,8192}"
end = ~"[01]{4}"
"""

由于数据字段长度可变,并且解析器贪婪,因此上述序列无法与上述语法匹配。解析器将结束字段位带入数据字段。

我把我的问题简化为上面的例子。

让我描述完整的问题。有3种数据包(让我们称之为令牌,握手和数据包)。令牌和握手包具有固定长度,并且数据包是可变长度的。 (上面显示的示例是数据包的示例)

输入由连续的比特流组成。每个数据包开头都标有“开始”模式,数据包结束标有“结束”模式。这两者都是固定的位模式。

示例令牌包语法:

start - 2 bits, id - 8 bits, address - 7bits, end - 4bits
111011010011011101100

握手包语法示例:

start - 2 bits, id - 8bits, end - 4 bits
11101101001100

顶级规则示例:

packet = tokenpacket | datapacket | handshakepacket

如果只有一种类型的数据包,那么切片就可以了。但是当我们开始解析时,我们不知道哪个数据包最终会最终匹配。这就是为什么我想使用语法,因为问题与语言解析非常相似。

在我们有3种不同的数据包类型需要解析的情况下,我们能否使切片方法有效?

解决这个问题的最佳方法是什么?

提前致谢,

3 个答案:

答案 0 :(得分:2)

这样做,只需使用切片来完成这项工作:

def binParser(data):
    result = {}
    result["start"] = data[:2]
    result["id"] = data[2:8]
    result["end"] = data[-4:]
    result["data"] = data[10:-4]
    return result

您将从字符串中获取正确的数据。

答案 1 :(得分:2)

据推测,只有一个可变长度的字段,所以你可以通过定义一个距离序列开始的距离和距离结尾的距离来允许这种情况,例如

rules = {'start': (None, 2), 'id': (2, 10), 
         'data': (10, -4), 'end': (-4, None)}

然后使用切片:

sequence =  '1110110100110111011011110101100101100'

result = dict((k, sequence[v[0]:v[1]]) for k, v in rules.items())

这给出了:

result == {'id': '10110100', 
           'end': '1100', 
           'data': '11011101101111010110010', 
           'start': '11'}

答案 2 :(得分:1)

既然您在标签中提到了pyparsing,那么我将使用pyparsing来解决这个问题。这使用Daniel Sanchez的binParser进行后期处理。

from pyparsing import Word

#Post-processing of the data.
def binParser(m):
    data = m[0]
    return {'start':data[:2],
            'id':data[2:8],
            'end':data[-4:],
            'data':data[10:-4]}
#At least 14 character for the required fields, attaching the processor
bin_sequence = Word('01',min=14).setParseAction(binParser)


sequence =  '1110110100110111011011110101100101100'
print bin_sequence.parseString(sequence)[0]

然后可以将其用作更大的解析器的一部分。