八位字节(字节)填充和取消填充,即用两个或v.v替换一个字节

时间:2012-11-23 13:26:34

标签: python list bytearray binary-data

我使用socket.recv()通过套接字(蓝牙)从设备接收二进制数据。

我已经考虑过在列表或bytearray中进行缓冲,直到收到足够的数据进行解码和操作。也就是说,直到我收到一个开始和停止标志(字节)。

然而,该设备正在应用“八位字节填充”。也就是说,下面两个字节的每次出现都应该用一个字节替换,如下所示:

dic = { '\xFE\xDC' : '\xFC' , '\xFE\xDD' : '\xFD' , '\xFE\xDE' : '\xFE'}

此外,在发送数据时,应该反过来。例如,一个字节0xFC - >产生两个字节0xFE,0xDC。

真正发生的是当填充(发送数据)时,如果检测到0xFC,0xFD,0xFE之一,则在字节之前添加0xFE,该字节本身是异或0x20。在取消填充(接收数据)时,0XFE被丢弃,后面的字节被异或为0x20。

要说我对Python相当新,那将是轻描淡写。我昨天开始编码,有一个界面启动并运行。然而,这有点棘手。

我知道我可以将数据放入字符串并进行替换。但是要将二进制数据打包成字符串,替换然后解压缩和解码似乎有点低效。

我还可以观看传入的数据,并在看到0xFE标志时动作。什么是好的是填充/取消列表,bytearray或其他什么的方法。

替换列表或bytearray中的单个字节似乎并不太难,但用两个或另一个替换一个...?

非常感谢任何帮助。

(顺便说一下,这是Python 2.7。)

1 个答案:

答案 0 :(得分:6)

概述

您需要包装字节流并转义特定值。此外,需要另一种方法:unescape控制代码并获取原始有效负载。您正在使用套接字。 socket-commands使用字符串参数。在python中,每个字符串基本上都是char* - 数组的包装器。

天真的方法

它是一个字符串,我们想要用其他值替换特定值。那么实现这一目标的最简单方法是什么?

def unstuff(self, s):
    return s.replace('\xFE\xDC', '\xFC').replace('\xFE\xDD', '\xFE').replace('\xFE\xDE', '\xFE')
def stuff(self, s):
    return s.replace('\xFC', '\xFE\xDC').replace('\xFD', '\xFE\xDD').replace('\xFE', '\xFE\xDE')

似乎很糟糕。每次替换调用时,都会创建一个新的字符串副本。

迭代

一种非常pythonic的方法是为这个特定问题定义一个迭代器:定义迭代器以将输入数据转换为所需的输出。

def unstuff(data):
    i = iter(data)
    dic = {'\xDC' : '\xFC', '\xDD' : '\xFD', '\xFE' : '\xDE'}
    while True:
        d = i.next()  # throws StopIteration on the end
        if d == '\xFE':
            d2 = i.next()
            if d2 in dic:
                yield dic[d2]
            else:
                yield '\xFE'
                yield d2
        else:
            yield d

def stuff(data):
    i = iter(data)
    dic = { '\xFC' : '\xDC', '\xFD' : '\xDD', '\xFE' : '\xDE' }
    while True: 
        d = i.next() # throws StopIteration on the end
        if d in dic:
            yield '\xFE'
            yield dic[d]
        else:
            yield d

def main():
    s = 'hello\xFE\xDCWorld'
    unstuffed = "".join(unstuff(s))
    stuffed = "".join(stuff(unstuffed))
    print s, unstuffed, stuffed

    # also possible
    for c in unstuff(s):
        print ord(c)

if __name__ == '__main__':
    main()

stuff()unstuff()需要迭代(list,string,...)并返回iterator-object。如果您想print结果或将其传递给socket.send,则需要将其转换回字符串(如"".join()所示)。每个意外数据都以某种方式处理:0xFE 0x__将逐字返回,如果它与任何模式都不匹配。

正则表达式

另一种方法是使用regular expressions。它有时是一个很大的主题和麻烦的来源,但我们可以保持简单:

import re

s = 'hello\xFE\xDCWorld' # our test-string

# read: FE DC or FE DD or FE DE
unstuff   = re.compile('\xFE\xDC|\xFE\xDD|\xFE\xDE')
# read: 
#  - use this pattern to match against the string
#  - replace what you have found (m.groups(0), whole match) with
#    char(ord(match[1])^0x20)
unstuffed = unstuff.sub(lambda m: chr(ord(m.group(0)[1])^0x20), s)

# same thing, other way around
stuff   = re.compile('\xFC|\xFD|\xFE')
stuffed = stuff.sub(lambda m: '\xFE' + chr(ord(m.group(0))^0x20), unstuffed)

print s, unstuffed, stuffed

如上所述,您必须在某处创建新字符串才能将其与套接字一起使用。至少,这种方法不会像s.replace(..).replace(..).replace(..)那样创建不必要的字符串副本。您应该将模式stuffunstuff保留在某处,因为构建这些对象相对较贵。

原生C函数

如果python中的某些内容会变慢,我们可能希望使用cpython并将其作为完整的C代码实现。基本上,我做第一次运行,计算我的书呆子多少字节,分配一个新字符串并进行第二次运行。我不习惯python-c-extensions,所以我不想分享这段代码。它似乎工作,见下一章

比较

最重要的优化规则之一:比较!每个测试的基本设置:

generate random binary data as a string
while less_than_a_second:
    unstuff(stuff(random_data))
    count += 1
return time_needed / count

我知道,设置不是最佳的。但是我们应该得到一些有用的结果:

Comparing 4 methods

我们看到了什么? Native是最快的方法,但只适用于非常小的字符串。这可能是因为python-interpreter:只需要一个函数调用而不是三个。但是大多数时候微秒足够快。在大约500字节之后,时间与天真的方法几乎相同。在实施中必须有一些深刻的魔法发生。与努力相比,迭代器和RegExp是不可接受的。

总结一下:使用天真的方法。很难让事情变得更好。另外:如果你只是猜测时间,你几乎总是错的。