在Python中将位转换为字节

时间:2015-02-06 17:05:34

标签: python python-3.x optimization bit-manipulation

我试图在Python 3.x中将位字符串转换为字节字符串。在每个字节中,位从高位到低位填充。如有必要,最后一个字节用零填充。位串最初存储为"集合" booleans或整数(0或1),我想返回一个"集合"整数在0-255范围内。通过集合,我的意思是列表或类似的对象,但不是字符串:例如,下面的函数返回一个生成器。

到目前为止,我能够获得的最快速度如下:

def bitsToBytes(a):
    s = i = 0
    for x in a:
        s += s + x
        i += 1
        if i == 8:
            yield s
            s = i = 0
    if i > 0:
        yield s << (8 - i)

我尝试了几种方法:使用枚举,建立列表而不是生成器,通过&#34;(s&lt;&lt; 1)|来计算s。 X&#34;而不是总和,一切似乎都有点慢。由于这个解决方案也是我发现的最简单和最简单的解决方案之一,我对它很满意。

但是,我想知道是否有更快的解决方案。特别是,是否有一个库例程可以更快地完成工作,最好是在标准库中?


输入/输出示例

[] -> []
[1] -> [128]
[1,1] -> [192]
[1,0,0,0,0,0,0,0,1] -> [128,128]

这里我用列表显示示例。发电机没问题。但是,字符串不会,然后有必要将类似列表的数据之间的转换和foth转换为字符串。

3 个答案:

答案 0 :(得分:3)

消耗8-er块中的位并忽略异常的最简单策略:

def getbytes(bits):
    done = False
    while not done:
        byte = 0
        for _ in range(0, 8):
            try:
                bit = next(bits)
            except StopIteration:
                bit = 0
                done = True
            byte = (byte << 1) | bit
        yield byte

用法:

lst = [1,0,0,0,0,0,0,0,1]
for b in getbytes(iter(lst)):
    print b

getbytes是一个生成器并接受一个生成器,也就是说,它适用于大型且可能无限的流。

答案 1 :(得分:2)

步骤1:添加缓冲区零

步骤2:反转位,因为您的字节顺序被反转

第3步:连接成一个字符串

步骤4:一次将8位保存到数组

第5步:

第6步:获利

def bitsToBytes(a):
    a = [0] * (8 - len(a) % 8) + a # adding in extra 0 values to make a multiple of 8 bits
    s = ''.join(str(x) for x in a)[::-1] # reverses and joins all bits
    returnInts = []
    for i in range(0,len(s),8):
        returnInts.append(int(s[i:i+8],2)) # goes 8 bits at a time to save as ints
    return returnInts

答案 2 :(得分:2)

使用itertools' grouper()` recipe

from functools import reduce
from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

bytes = [reduce(lambda byte, bit: byte << 1 | bit, eight_bits)
         for eight_bits in grouper(bits, 8, fillvalue=0)]

Example

[] -> []
[1] -> [128]
[1, 1] -> [192]
[1, 0, 0, 0, 0, 0, 0, 0, 1] -> [128, 128]

如果输入是一个字符串,那么专门的解决方案可能会更快:

>>> bits = '100000001'
>>> padded_bits = bits + '0' * (8 - len(bits) % 8)
>>> padded_bits
'1000000010000000'
>>> list(int(padded_bits, 2).to_bytes(len(padded_bits) // 8, 'big'))
[128, 128]

如果len(bits) % 8 == 0,则最后一个字节为零。