Python位列表到字节lis

2024-06-17 09:10:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个长的一维整数1和0的列表,代表8位二进制字节。从中创建一个包含整数字节的新列表的方法是什么。在

我熟悉C,但对Python还不熟悉,所以我用C编写代码:一个经过每一位循环的复杂结构。然而,我知道Python over C的全部意义在于,这些事情通常可以紧凑而优雅地完成,我应该学习如何做到这一点。也许用列表理解法?在

这是可行的,但建议采用更“Python式”的方法,我们将不胜感激:

#!/usr/bin/env python2
bits = [1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1,1]
bytes = []
byt = ""
for bit in bits:
  byt += str(bit)
  if len(byt) == 8:
    bytes += [int(byt, 2)]
    byt = ""
print bytes

$ bits-to-bytes.py
[149, 107, 231]

Tags: 方法代码列表字节bytes二进制bit代表
3条回答

您可以将列表分成8个元素的块,并将子元素映射到str:

[int("".join(map(str, bits[i:i+8])), 2) for i in range(0, len(bits), 8)]

您可以将其分为两部分:映射和连接一次:

^{pr2}$

或者使用iter并借用itertools中的grouperrecipe

it = iter(map(str, bits))
[int("".join(sli), 2) for sli in zip(*iter([it] * 8))]

iter(map(str, bits))将位的内容映射到str并创建一个iteratorzip(*iter([it] * 8))将元素分成8个子元素的组。
每个zip(*iter..使用迭代器中的八个子元素,因此我们总是得到顺序组,这与第一个代码中的切片逻辑相同,我们只是避免了切片的需要。在

正如Sven所说,对于不能被n整除的列表,使用zip将丢失与原始代码类似的数据,您可以调整我链接的grouper配方来处理这些情况:

from itertools import zip_longest # izip_longest python2

bits = [1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1,1,0]
it = iter(map(str, bits))

print( [int("".join(sli), 2) for sli in izip_longest(*iter([it] * 8),fillvalue="")])
[149, 107, 231, 2] # using just zip would be  [149, 107, 231] 

fillvalue=""意味着我们用空字符串填充奇数长度组,这样我们仍然可以调用int("".join(sli), 2)并获得正确的输出,如上面所示,在获取3 * 8块之后,我们只剩下1,0。在

在您自己的代码中,bytes += [int(byt, 2)]可以简单地变成bytes.append(int(byt, 2))

Padraic的解决方案很好;还有另一种方法:

from itertools import izip_longest


def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # Taken from itertools recipes
    # https://docs.python.org/2/library/itertools.html#recipes
    # grouper('ABCDEFG', 3, 'x')  > ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

bits = [1, 0, 0, 1, 0, 1, 0, 1,
        0, 1, 1, 0, 1, 0, 1, 1,
        1, 1, 1, 0, 0, 1, 1, 1]

byte_strings = (''.join(bit_group) for bit_group in grouper(map(str, bits), 8))
bytes = [int(byte_string, 2) for byte_string in byte_strings]

print bytes  # [149, 107, 231]

由于从数字列表开始,您可能希望避免字符串操作。这里有两种方法:

  • 将原始列表分为8位块并计算每个字节的十进制值(假设位的数量是8的倍数);感谢Padraic Cunningham用8个子元素的组来划分序列的好方法

    bits = [1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1,1]
    [sum(b*2**x for b,x in zip(byte[::-1],range(8))) for byte in zip(*([iter(bits)]*8))]
    
  • 使用位运算符(可能效率更高);如果位数不是8的倍数,则代码的工作方式就好像比特序列在左边填充了0(左边的填充通常比右边的填充更有意义,因为它保留了原始二进制数字序列的数值

    bits = [1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1,1]
    n = sum(b*2**x for b,x in zip(bits[::-1],range(len(bits)))) # value of the binary number represented by 'bits'
    # n = int(''.join(map(str,bits)),2) # another way of finding n by means of string manipulation
    [(n>>(8*p))&255 for p in range(len(bits)//8-(len(bits)%8==0),-1,-1)]
    

相关问题 更多 >