我需要能够划分二进制数据流。我正在考虑使用像ASCII EOT(传输结束)这样的字符来做这件事。
然而,我有点担心 - 我怎么能确定用于此(0b00000100)的特定二进制序列不会出现在我自己的二进制序列中,从而在分界上给出误报?
换句话说,如何最好地处理二进制分隔?
编辑:...不使用长度标题。对不起,伙计们,之前应该提到过这个。
答案 0 :(得分:12)
你有五个选择:
\0
在某些内容中包含ASCII NUL。)答案 1 :(得分:8)
通常,您以一种众所周知的格式包装二进制数据,例如使用描述后续数据的固定标头。如果您试图在未知数据流中找到分隔符,通常需要转义序列。例如,像HDLC,其中0x7E是帧分隔符。必须对数据进行编码,使得如果数据内部存在0x7E,则将其替换为0x7D,然后是原始数据的XOR。类似地转义数据流中的0x7D。
答案 2 :(得分:3)
如果二进制记录确实可以包含任何数据,请尝试在数据之前添加长度,而不是在数据之后添加标记。这有时称为前缀长度,因为长度在数据之前。
否则,您必须转义字节流中的分隔符(并转义转义序列)。
答案 3 :(得分:3)
您可以在它之前添加二进制数据的大小。如果您正在处理流数据并且事先不知道它的大小,则可以将其划分为块并使每个块以size字段开头。
如果你为一个块设置了一个最大大小,你最终会得到除了最后一个块之外的所有长度相同的长度,这样可以在需要时简化随机访问。
答案 4 :(得分:0)
作为节省空间和固定开销的替代方法,可以在数据前添加大小字段并转义分隔符,escapeless编码可用于修剪该分隔符,可能与其他应根据您的数据具有特殊含义。
答案 5 :(得分:0)
@sarnold的答案很好,在这里我想分享一些代码来说明它。
首先,这是一种错误的方法:使用\n
分隔符。别做!二进制数据可以包含\n
,它将与定界符混合在一起:
import os, random
with open('test', 'wb') as f:
for i in range(100): # create 100 binary sequences of random
length = random.randint(2, 100) # length (between 2 and 100)
f.write(os.urandom(length) + b'\n') # separated with the character b"\n"
with open('test', 'rb') as f:
for i, l in enumerate(f):
print(i, l) # oops we get 123 sequences! wrong!
...
121 b“ L \ xb1 \ xa6 \ xf3 \ x05b \ xc9 \ x1f \ x17 \ x94'\ n”
122 b'\ xa4 \ xf6 \ x9f \ xa5 \ xbc \ x91 \ xbf \ x15 \ xdc} \ xca \ x90 \ x8a \ xb3 \ x8c \ xe2 \ x07 \ x96 <\ xeft \ n'
现在以正确的方式进行操作(萨尔诺德答案中的选项4):
import os, random
with open('test', 'wb') as f:
for i in range(100):
length = random.randint(2, 100)
f.write(length.to_bytes(2, byteorder='little')) # prepend the data with the length of the next data chunk, packed in 2 bytes
f.write(os.urandom(length))
with open('test', 'rb') as f:
i = 0
while True:
l = f.read(2) # read the length of the next chunk
if l == b'': # end of file
break
length = int.from_bytes(l, byteorder='little')
s = f.read(length)
print(i, s)
i += 1
...
98 b“ \ xfa6 \ x15CU \ x99 \ xc4 \ x9f \ xbe \ x9b \ xe6 \ x1e \ x13 \ x88X \ x9a \ xb2 \ xe8 \ xb7(K'\ xf9 + X \ xc4”
99 b'\ xaf \ xb4 \ x98 \ xe2 * HInHp \ xd3OxUv \ xf7 \ xa7 \ x93Qf ^ \ xe1C \ x94J)'