Преобразование 64-битного целого числа в 8 отдельных 1 байтовых целых чисел в python

В python мне дали 64-битное целое число. Это целое было создано, взяв несколько различных 8-битных целых чисел и объединив их в одно гигантское 64-битное целое число. Это моя работа, чтобы отделить их снова.

Например:

Source number: 2592701575664680400
Binary (64 bits): 0010001111111011001000000101100010101010000101101011111000000000
int 1: 00100011 (35)
int 2: 11111011 (251)
int 3: 00100000 (32)
int 4: 01011000 (88)
int 5: 10101010 (170)
int 6: 00010110 (22)
int 7: 10111110 (190)
int 8: 00000000 (0)

Итак, что я хотел бы сделать, это взять исходный номер 2592701575664680373 и вернуть массив длиной 8, где каждый int в массиве - это те, которые перечислены выше.

Я собирался использовать struct, но, если быть честным, прочитав документацию, я не понял, как именно это сделать.

Ответ 1

В Python 2.x, struct.pack возвращает строку байтов. Легко преобразовать это в массив целых чисел.

>>> bytestr = struct.pack('>Q', 2592701575664680400)
>>> bytestr
'#\xfb X\xaa\x16\xbd\xd0'
>>> [ord(b) for b in bytestr]
[35, 251, 32, 88, 170, 22, 189, 208]

Модуль struct в python используется для преобразования из объекта python в байтовые строки, обычно упакованы в соответствии с правилами упаковки структуры C. struct.pack принимает спецификатор формата (строка, которая описывает, как должны быть выложены байты структуры) и некоторые данные python, и упаковывает их в строку байтов. struct.unpack выполняет инверсию, беря спецификатор формата и байтовую строку и снова возвращая кортеж распакованных данных в формате объектов python.

Используемый спецификатор формата состоит из двух частей. Ведущий символ указывает континент (порядок байтов) строки. Следующие символы указывают типы полей структуры, которые упакованы или распакованы. Таким образом, '>Q' означает упаковать данные в формате big-endian unsigned long long. Чтобы получить байты в обратном порядке, вы можете использовать < вместо этого для little-endian.

Последняя операция - это понимание списка, которое выполняет итерацию над символами строки байта и использует встроенную функцию ord для получения целочисленного представления этого символа.

Заключительное примечание: у Python фактически нет понятия целочисленного размера. В 2.x существует int, который ограничен 32 битами, и long, который имеет неограниченный размер. В 3.x эти два были объединены в один тип. Поэтому, хотя эта операция гарантирует получение целых чисел, которые занимают только один байт, уведомление о python заставит результирующие целые числа оставаться такими, если вы используете их в других операциях.

Ответ 2

Решение

Решение без преобразования числа в строку:

x = 0b0010001111111011001000000101100010101010000101101011111000000000

numbers = list((x >> i) & 0xFF for i in range(0,64,8))
print(numbers)                    # [0, 190, 22, 170, 88, 32, 251, 35]
print(list(reversed(numbers)))    # [35, 251, 32, 88, 170, 22, 190, 0]

Объяснение

Здесь я использовал понимание списков, создавая цикл с шагом 8 по i. Итак, i принимает значения 0, 8, 16, 24, 32, 40, 48, 56. Каждый раз оператор битрейта >> временно смещает число x вниз на i бит. Это эквивалентно делению на 256^i.

Итак, получившееся число:

i = 0:   0010001111111011001000000101100010101010000101101011111000000000
i = 8:           00100011111110110010000001011000101010100001011010111110
i = 16:                  001000111111101100100000010110001010101000010110
i = 24:                          0010001111111011001000000101100010101010
i = 32:                                  00100011111110110010000001011000
i = 40:                                          001000111111101100100000
i = 48:                                                  0010001111111011
i = 56:                                                          00100011

В usig & 0xFF я выбираю последние 8 бит этого числа. Пример:

x >> 48:           001000111111101100100000
0xff:                              11111111
(x >> 48) & 0xff:  000000000000000000100000

Так как ведущие нули не имеют значения, у вас есть нужный номер.

Результат преобразуется в список и печатается в нормальном и обратном порядке (например, OP хотел).

Производительность

Я сравнил время этого результата с другими решениями, предложенными в этом потоке:

In: timeit list(reversed([(x >> i) & 0xFF for i in range(0,64,8)]))
100000 loops, best of 3: 13.9 µs per loop

In: timeit [(x >> (i * 8)) & 0xFF for i in range(7, -1, -1)]
100000 loops, best of 3: 11.1 µs per loop

In: timeit [(x >> i) & 0xFF for i in range(63,-1,-8)]
100000 loops, best of 3: 10.2 µs per loop

In: timeit reversed(struct.unpack('8B', struct.pack('Q', x)))
100000 loops, best of 3: 3.22 µs per loop

In: timeit reversed(struct.pack('Q', x))
100000 loops, best of 3: 2.07 µs per loop

Результат: мое решение не самое быстрое! В настоящее время использование struct напрямую (как предложено Mark Ransom) представляется самым быстрым фрагментом.

Ответ 3

bn = "0010001111111011001000000101100010101010000101101011111000000000"

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)])
[35, 251, 32, 88, 170, 22, 190, 0]

Если вы используете двоичное представление n, тогда вывод будет другим:

n = 2592701575664680373
bn = bin(n)

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)])
[35, 251, 32, 88, 170, 22, 189, 181]

Некоторые тайминги:

In [16]: %%timeit                                                
numbers = list((n >> i) & 0xFF for i in range(0,64,8))
list(reversed(numbers))
   ....: 
100000 loops, best of 3: 2.97 µs per loop

In [17]: timeit [(n >> (i * 8)) & 0xFF for i in range(7, -1, -1)]
1000000 loops, best of 3: 1.73 µs per loop

In [18]: %%timeit                                                
bn = bin(n)
[int(bn[i:i+8], 2) for i in range(0,len(bn), 8)]
   ....: 
100000 loops, best of 3: 3.96 µs per loop

Вы также можете просто divmod:

out = []
for _ in range(8):
    n, i = divmod(n, 256)
    out.append(i) 
out = out[::-1]

Это почти так же эффективно:

In [31]: %%timeit
   ....: n = 2592701575664680411
   ....: out = []
   ....: for _ in range(8):
   ....:     n, i = divmod(n, 1 << 8)
   ....:     out.append(i)
   ....: out[::-1]
   ....: 
100000 loops, best of 3: 2.35 µs per loop

В смещении битов с помощью python очень мало преимуществ, я был бы более склонен использовать то, что вы и другие найдете более читаемыми.

Ответ 4

Здесь версия с использованием struct:

import struct
n = 2592701575664680400
bytes = struct.unpack('8B', struct.pack('Q', n))

bytes возвращаются в обратном порядке, указанном в вашем вопросе.

Вот статистика производительности:

python -m timeit -s "import struct" "struct.unpack('8B', struct.pack('Q', 2592701575664680400))"
1000000 loops, best of 3: 0.33 usec per loop

На моем компьютере это в три раза быстрее, чем решение для преобразования байтов.