我使用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。)
答案 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(..)
那样创建不必要的字符串副本。您应该将模式stuff
和unstuff
保留在某处,因为构建这些对象相对较贵。
如果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
我知道,设置不是最佳的。但是我们应该得到一些有用的结果:
我们看到了什么? Native是最快的方法,但只适用于非常小的字符串。这可能是因为python-interpreter:只需要一个函数调用而不是三个。但是大多数时候微秒足够快。在大约500字节之后,时间与天真的方法几乎相同。在实施中必须有一些深刻的魔法发生。与努力相比,迭代器和RegExp是不可接受的。
总结一下:使用天真的方法。很难让事情变得更好。另外:如果你只是猜测时间,你几乎总是错的。