【问题标题】:Python Convert a list of binary to stringPython将二进制列表转换为字符串
【发布时间】:2020-06-20 20:04:30
【问题描述】:

我有一个 1 和 0 的列表 --> output = [1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0]

我想将 1 和 0 的列表转换为字符串,其中“litlle-endian”中的每 8 位代表“latin1”中的一个字母

到目前为止,我的这段代码(如下)运行良好,但我认为它很慢并且似乎减慢了我的脚本......

for i in range(0,len(output),8):
        x=output[i:i+8]
        l="".join([str(j) for j in x[::-1]])
        out_str += chr(int(("0b"+l),base=2))

你有更快的想法吗?

【问题讨论】:

  • 你用这个程序做什么?您是否在数千个不同值的列表中反复执行此操作?我必须运行您的样本 100,000 次(!)才能获得超过亚秒级的时间。如果输出是文本,我会解码 200,000 个字符——一本书中大约有一千

标签: python python-3.x string list binary


【解决方案1】:

这是一个更快的解决方案,它使用包含 256 个可能字符的元组字典:

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

chars = { tuple(map(int,f"{n:08b}"[::-1])):chr(n) for n in range(0,256) }

def toChars(bits):
    return "".join(chars[tuple(bits[i:i+8])] for i in range(0,len(bits),8) )

比原来的解决方案快大约 3 倍

[EDIT] 和一个使用字节和 zip 的更快的:

chars = { tuple(map(int,f"{n:08b}")):n for n in range(256) }
def toChars(bits):
    return bytes(chars[b] for b in zip(*(bits[7-i::8] for i in range(8)))).decode()

大约比前一个快 2 倍(在长列表中)

[EDIT2] 对这最后一个的一点解释...

  • 列表理解中的b 将是一个 8 位元组
  • chars[b]会返回8位对应的整数
  • bytes(...).decode() 根据每个值的 chr(n) 将整数列表转换为字符串
  • zip(*(... 8 bit iterators...)) 解压缩并行运行的 8 个跨度范围的位,每个范围都从不同的起点开始

解压 zip 的策略是以 8 为步长遍历位。例如,如果我们遍历 8 个并行范围,我们会得到:

 bits[7::8] -> [ 0, 0, ... ]  zip returns: (0,1,0,0,0,1,1)
 bits[6::8] -> [ 1, 1, ... ]               (0,1,1,0,1,1,1)
 bits[5::8] -> [ 0, 1, ... ]               ...
 bits[4::8] -> [ 0, 0, ... ]
 bits[3::8] -> [ 0, 1, ... ]
 bits[2::8] -> [ 0, 1, ... ]
 bits[1::8] -> [ 1, 1, ... ]
 bits[0::8] -> [ 1, 1, ... ] 

zip 函数将在每次迭代中提取一列并将其作为位元组返回。

【讨论】:

  • 很好的解决方案!我花了一些时间来掌握它。我用你的代码的菜鸟版本编辑了我的答案:-)
【解决方案2】:
#!/usr/bin/python                                                                                                                                                                                                                                                                                                                                                         

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

c = 0
for i,v in enumerate(bits):
    i = i % 8
    c = c | v << i
    if i == 7:
        result.append(chr(c))
        c = 0

print(''.join(result))

测试:

$ python ./test.py
Co

【讨论】:

    【解决方案3】:

    使用sumenumerate 应该更快,因为它们是内置的。让你和我在同一台机器上计时。

    循环运行 100,000 次,并使用 time python3 tmp.py 进行测试。 (user 值。sys 的时间量都在 0m0.012s 左右徘徊,因此它对结果的影响只有百分比。)

    你的:0m1.624s
    我的速度快了 50%:0m1.063s,有了这个

    output = [1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0]
    
    for item in [output[i:i + 8] for i in range(0, len(output), 8)]:
        out_str += chr(sum(x<<i for i,x in enumerate(item)))
    

    【讨论】:

    • ... 因为它只有 8 个值,所以 [x
    【解决方案4】:

    我对所有有效解决方案的执行时间进行了一些测量。在代码中查看下面的结果。代码从最慢到最快排序。最快的是来自Alain T. 的那个。我在一个相当大的列表上测试了代码,得到了 200000 个字符的字符串。

    即使对于这么大的列表,对于我原来的解决方案来说,执行时间仍然非常快。我的程序中的其他地方一定有问题...... :-)

    谢谢大家的代码!

    import time
    start_time = time.time()
    bits = [1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0] * 100000
        ### tested code ###
    print("Execution time: ", time.time() - start_time, "seconds")
    
    
    ### former solution --> 0.59 seconds
    out_str = ""
    for i in range(0,len(bits),8):
        x=bits[i:i+8]
        l="".join([str(j) for j in x[::-1]])
        out_str += chr(int(("0b"+l),base=2))
    
    
    ### enumerate and result.append --> 0.48 seconds
    result = []
    c = 0
    for i,v in enumerate(bits):
        i = i % 8
        c = c | v << i
        if i == 7:
            result.append(chr(c))
            c = 0
    out_str = ''.join(result)
    
    
    ### sum and enumerate --> 0.45 seconds
    out_str = ""
    for item in [bits[i:i + 8] for i in range(0, len(bits), 8)]:
        out_str += chr(sum(x<<i for i,x in enumerate(item)))
    
    
    ### map and chars dictionary --> 0.10 seconds
    chars = { tuple(map(int,f"{n:08b}"[::-1])):chr(n) for n in range(0,256) }
    def toChars(bits):
        return "".join(chars[tuple(bits[i:i+8])] for i in range(0,len(bits),8) )
    
    
    ### bytes and zip --> 0.06 seconds
    chars = { tuple(map(int,f"{n:08b}")):n for n in range(256) }
    def toChars(bits):
        return bytes(chars[b] for b in zip(*(bits[7-i::8] for i in range(8)))).decode()
    

    编辑:

    我以更易于理解的形式(不使用列表推导)编写了最好(最快)的解决方案,因此我可以逐步完成代码,因为我花了一些时间来理解它是如何工作的(Alain T. 的解决方案):

    bits = [1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0] * 10
    chars = {tuple(map(int,f"{n:08b}")):n for n in range(256)}
    temp = []
    out = []
    for i in range(8):
        temp.append(bits[7-i::8])
    unzipped = zip(*temp)
    for b in unzipped:
        out.append(bytes([chars[b]]).decode())
    print("".join(out))
    

    【讨论】:

      【解决方案5】:

      检查这是否更快:

      tmp_list = []
      for i in range(0,len(output),8):
          byte_value = 0
          for digit in output[i:i+8:-1]:
              byte_value = (byte_value<<1) + digit
          tmp_list.append(chr(byte_value))
      out_str = ''.join(tmp_list)
      

      【讨论】:

      • 循环的第二个不应该是:for digit in output[i+8:i:-1]吗?但无论如何,它似乎不起作用。这个结果应该是"Co"
      猜你喜欢
      • 1970-01-01
      • 2014-11-07
      • 1970-01-01
      • 2010-11-22
      • 2021-02-24
      • 2017-05-26
      • 1970-01-01
      相关资源
      最近更新 更多