Python Преобразование списка двоичных файлов в строку - PullRequest
1 голос
/ 08 марта 2020

У меня есть список из 1 и 0 -> output = [1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0]

Я хотел бы преобразовать этот список единиц и нулей в строку, где каждые 8 ​​бит в "litlle-endian" представляют один буква "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))

Do у вас есть идеи быстрее?

Ответы [ 5 ]

3 голосов
/ 09 марта 2020

Вот более быстрое решение с использованием словаря кортежей для 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-кодом заключается в go через биты с шагом 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 примет один столбец этого за итерацию и возвратит это как кортеж битов.

1 голос
/ 10 марта 2020

Я сделал некоторые измерения времени выполнения для всех допустимых решений. Смотрите результаты ниже в коде. Коды сортируются от самого медленного до самого быстрого. Самый лучший из них - Алена Т. . Я проверил коды в довольно большом списке, в результате чего получилась строка из 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))
1 голос
/ 09 марта 2020
#!/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
1 голос
/ 09 марта 2020

Использование sum и enumerate должно быть быстрее, поскольку они являются встроенными. Давайте посчитаем время на вашем и на одной машине.

Выполните 100 000 раз за все oop и протестируйте с time python3 tmp.py. (user значения. Для обоих значений sys время колебалось около 0m0,012 с, поэтому оно оказывало только процентное влияние на результаты.)

Ваше: 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)))
0 голосов
/ 09 марта 2020

Проверьте, быстрее ли это:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...