【问题标题】:Delimiting binary sequences分隔二进制序列
【发布时间】:2012-01-22 10:16:54
【问题描述】:

我需要能够分隔二进制数据流。我正在考虑使用类似 ASCII EOT(传输结束)字符来执行此操作。

但是我有点担心——我怎么能确定用于此的特定二进制序列 (0b00000100) 不会出现在我自己的二进制序列中,从而在定界时给出误报?

换句话说,如何最好地处理二进制定界?

编辑:...不使用长度标题。对不起,伙计们,之前应该提过这个的。

【问题讨论】:

    标签: networking binary binary-data delimiter


    【解决方案1】:

    @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

    现在是正确的方法(sarnold 回答中的选项#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)'

    【讨论】:

      【解决方案2】:

      作为一种节省空间和固定开销的替代方案,可以在数据前面加上大小字段并转义分隔符,escapeless 编码可用于修剪该分隔符,可能与其他应该具有特殊的字符一起使用意思是,根据您的数据。

      【讨论】:

        【解决方案3】:

        您有五个选择:

        • 使用不太可能出现的分隔符。这冒着你猜错的风险。我不推荐这种方法。
        • 使用分隔符和escape sequence 包含分隔符。您可能需要将转义字符加倍,具体取决于使解析更容易的原因。 (想想 C \0 在某些内容中包含 ASCII NUL。)
        • 使用您可以确定不会出现的分隔符短语。 (想想mime message boundaries。)
        • 预先添加一个 length 字段,这样您就知道要读取以下 N 个字节作为数据。这样做的缺点是要求您在写入数据之前知道这个长度,这有时很困难或不可能。
        • 使用更复杂的东西,例如ASN.1,为您完整地描述所有您的内容。 (我不知道我是否真的会推荐这个,除非你可以很好使用它——ASN.1 在最好的情况下使用起来很尴尬,但它确实允许完全明确的二进制数据解释。)

        【讨论】:

          【解决方案4】:

          通常,您以众所周知的格式包装二进制数据,例如使用描述后续数据的固定标头。如果您尝试在未知数据流中查找分隔符,通常需要转义序列。例如,像 HDLC 这样的东西,其中 0x7E 是帧分隔符。必须对数据进行编码,如果数据中有 0x7E,则将其替换为 0x7D,后跟原始数据的 XOR。数据流中的 0x7D 也同样被转义。

          【讨论】:

          • 你能解释一下这里的逻辑吗?鉴于选择 0x7E 作为帧分隔符,为什么用 0x7D 和 XORed 值替换流中的这个字节?为什么不简单地用 0x7E0x7E 替换? XORed 值是如何精确计算的?当流包含 0x7D 时会发生什么?您如何区分流中的这种“自然”和作为 0x7E 的替代品?
          【解决方案5】:

          您可以在其前面添加二进制数据的大小。如果您正在处理流式数据并且事先不知道其大小,则可以将其分成块并让每个块以 size 字段开头。

          如果您为一个块设置了最大大小,那么除了最后一个块之外,您最终会得到相同的长度,这将在您需要时简化随机访问。

          【讨论】:

            【解决方案6】:

            如果二进制记录确实可以包含任何数据,请尝试在数据之前添加长度而不是在数据之后添加标记。这有时被称为前缀长度,因为长度在数据之前。

            否则,您必须转义字节流中的分隔符(并转义转义序列)。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-08-03
              • 1970-01-01
              • 1970-01-01
              • 2020-07-18
              • 1970-01-01
              • 2021-09-04
              • 2018-08-31
              • 2018-01-12
              相关资源
              最近更新 更多