将ascii字节串打包成bitmap / bitstring返回字符串?

时间:2015-02-28 22:26:56

标签: python string bitmap bitstring

我有一个打包的字符串,使得每个字符最初都是无符号字节,但存储为7位,然后打包成无符号字节数组。我试图找到一种在Python中解压缩这个字符串的快捷方法,但我编写的使用bitstring模块的函数运行良好,但速度非常慢。似乎这样的事情不应该这么慢,但我可能效率很低......

这看起来像是微不足道的东西,但我只是不知道该使用什么,也许已经有一个函数会解压缩字符串?

from bitstring import BitArray

def unpackString(raw):
    msg = ''

    bits = BitArray(bytes=raw)
    mask = BitArray('0b01111111')

    i = 0
    while 1:
        try:
            iByte = (bits[i:i + 8] & mask).int
            # value of 0 denotes a line break
            if iByte == 0:
                msg += '\n'
            elif iByte >= 32 and iByte <= 126:
                msg += chr(iByte)
            i += 7
        except:
            break

    return msg

1 个答案:

答案 0 :(得分:2)

我花了一些时间才弄明白,因为你的解决方案似乎忽略了第一位数据。给定输入字节129(0b10000001)我希望看到64 '1000000'由以下内容打印,但是您的代码会生成1 '0000001' - 忽略第一位。

bs = b'\x81' # one byte string, whose value is 129 (0x81)
arr = BitArray(bs)
mask = BitArray('0b01111111')
byte = (arr[0:8] & mask).int
print(byte, repr("{:07b}".format(byte)))

最简单的解决方案是修改您的解决方案以使用bitstring.ConstBitStream - 我通过以下方式提高了一个数量级的速度。

from bitstring import ConstBitStream
def unpack_bitstream(raw):
    num_bytes, remainder = divmod(len(raw) * 8 - 1, 7)
    bitstream = ConstBitStream(bytes=raw, offset=1) # use offset to ignore leading bit
    msg = b''
    for _ in range(num_bytes):
        byte = bitstream.read("uint:7")
        if not byte:
            msg += b'\n'
        elif 32 <= byte <= 126:
            msg += bytes((byte,))
            # msg += chr(byte) # python 2
    return msg

但是,只使用标准库就可以轻松完成。这使得解决方案更具可移植性,并且在我尝试的实例中,速度提高了一个数量级(我没有尝试bitstring的cython版本。)

def unpack_bytes(raw, zero_replacement=ord("\n")):
    # use - 1 to ignore leading bit
    num_bytes, remainder = divmod(len(raw) * 8 - 1, 7)

    i = int.from_bytes(raw, byteorder="big")
    # i = int(raw.encode("hex"), 16) # python 2
    if remainder:
        # remainder means there are unused trailing bits, so remove these
        i >>= remainder

    msg = []
    for _ in range(num_bytes):
        byte = i & 127
        if not byte:
            msg.append(zero_replacement)
        elif 32 <= byte <= 126:
            msg.append(byte)
        i >>= 7
    msg.reverse()

    return bytes(msg)
    # return b"".join(chr(c) for c in msg) # python 2

我使用python 3来创建这些方法。如果你正在使用python 2,那么你需要进行一些调整。我已将这些作为评论添加到他们要替换的行之后并将其标记为python 2