3

各文字が元々符号なしバイトであったが、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
4

1 に答える 1

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

ただし、これは標準ライブラリのみを使用して非常に簡単に行うことができます。これにより、ソリューションの移植性が向上し、私が試した例では、さらに桁違いに高速になりました (私は の cythonized バージョンを試していませんでしたbitstring)。

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

于 2015-03-01T11:45:24.147 に答える