Упаковка 4 целых чисел как один байт? - PullRequest
16 голосов
/ 14 марта 2011

У меня есть четыре целых числа {a, b, c, d}, которые могут иметь следующий диапазон значений:

a - {0 или 1} (1 бит)

b - {0 или 1} (1 бит)

c - {0, 1, 2, ..., 7} (3 бита)

d - {0, 1, 2, ..., 7} (3 бита)

сначала я хотел бы упаковать их в один байт, который затем можно записать в двоичный файл. позже я хотел бы распаковать этот один байт и получить из него кортеж в форме (a, b, c, d).

Я знаю, как читать / записывать байт в двоичный файл на python. Но как мне сделать упаковку / распаковку битов?

Ответы [ 4 ]

31 голосов
/ 14 марта 2011

Используйте сдвиг и побитовое ИЛИ, затем преобразуйте в символ, чтобы получить «байт»:

x = chr(a | (b << 1) | (c << 2) | (d << 5))

Чтобы снова распаковать этот байт, сначала преобразуйте в целое число, затем сдвиньте и используйте побитовое И:

i = ord(x)
a = i & 1
b = (i >> 1) & 1
c = (i >> 2) & 7
d = (i >> 5) & 7

Объяснение: Изначально у вас есть

0000000a
0000000b
00000ccc
00000ddd

Сдвиг влево дает вам

0000000a
000000b0
000ccc00
ddd00000

Побитовое ИЛИ приводит к

dddcccba

Преобразование в символ преобразует это в один байт.

Распаковка: четыре различных сдвига вправо приводят к

dddcccba
0dddcccb
00dddccc
00000ddd

Маскированию (побитовое И) с 1 (0b00000001) или 7 (0b00000111) приводит к

0000000a
0000000b
00000ccc
00000ddd

снова.

10 голосов
/ 14 марта 2011
def encode(a, b, c, d):
  return a | b << 1 | c << 2 | d << 5

def decode(x):
  return x & 1, (x >> 1) & 1, (x >> 2) & 7, (x >> 5) & 7
5 голосов
/ 14 марта 2011

Если вам нужно много подобных вещей, то сдвиг битов может стать утомительным и подверженным ошибкам.Существуют сторонние библиотеки, которые могут помочь - я написал библиотеку bitstring :

Для упаковки и преобразования в байт:

x = bitstring.pack('2*uint:1, 2*uint:3', a, b, c, d).bytes

и для распаковки:

a, b, c, d = bitstring.BitArray(bytes=x).unpack('2*uint:1, 2*uint:3')

Это, вероятно, излишне для вашего примера, но полезно, когда все становится сложнее.

4 голосов
/ 14 марта 2011

Довольно просто.Маска (для диапазона), сдвиньте их на место и / или их вместе.

packed = ((a & 1) << 7) | ((b & 1) << 6) | ((c & 7) << 3) | (d & 7)

a = (packed >> 7) & 1
b = (packed >> 6) & 1
c = (packed >> 3) & 7
d = packed & 7
...