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