Python: извлечение битов из байта

Я читаю двоичный файл в python, и в документации по формату файла говорится:

Флаг (в двоичном формате) Значение

1 nnn nnnn Указывает, что следует один байт данных, который следует дублировать nnn nnnn (максимум 127) раз.

0 nnn nnnn Указывает, что далее следует nnn nnnn байтов данных изображения (максимум 127 байтов) и что дубликатов нет.

n 000 0000 Поле конца строки. Указывает на конец строки записи. Значение n может быть равно нулю или единице. Обратите внимание, что поле конца строки обязательно и что оно отражено в поле записи длины строки, упомянутом выше.

При чтении файла я ожидаю, что байт, в котором я нахожусь, вернет 1 nnn nnnn, где часть nnn nnnn должна быть 50.

Я смог сделать это, используя следующее:

flag = byte >> 7
numbytes = int(bin(byte)[3:], 2)

Но вычисление numbytes кажется дешевым обходным путем.

Могу ли я сделать больше битовой математики, чтобы выполнить расчет numbytes?

Как бы вы подошли к этому?


person Evan Borgstrom    schedule 30.03.2012    source источник
comment


Ответы (7)


Вы можете удалить начальный бит, используя маску AND с байтом из файла. Это оставит вам значение оставшихся битов:

mask =  0b01111111
byte_from_file = 0b10101010
value = mask & byte_from_file
print bin(value)
>> 0b101010
print value
>> 42

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

РЕДАКТИРОВАТЬ: чуть более полный пример для вашего варианта использования:

LEADING_BIT_MASK =  0b10000000
VALUE_MASK = 0b01111111

values = [0b10101010, 0b01010101, 0b0000000, 0b10000000]

for v in values:
    value = v & VALUE_MASK
    has_leading_bit = v & LEADING_BIT_MASK
    if value == 0:
        print "EOL"
    elif has_leading_bit:
        print "leading one", value
    elif not has_leading_bit:
        print "leading zero", value
person alan    schedule 30.03.2012
comment
Спасибо. Я тоже предпочитаю двоичные числа в этом случае. - person Evan Borgstrom; 30.03.2012
comment
@EvanBorgstrom В Python 3 вы можете написать: data = b'\xaa\x55\x00\x80' и for byte in data: .... Не используйте имя bytes, это встроенный тип. - person jfs; 12.05.2016

Классический подход к проверке того, установлен ли бит, заключается в использовании бинарного оператора «и», т.е.

x = 10 # 1010 in binary
if x & 0b10:  # explicitly: x & 0b0010 != 0
    print('First bit is set')

Чтобы проверить, установлен ли n ^-й бит, используйте степень двойки или лучше сдвиг битов

def is_set(x, n):
    return x & 2 ** n != 0 

    # a more bitwise- and performance-friendly version:
    return x & 1 << n != 0

is_set(10, 1) # 1 i.e. first bit - as the count starts at 0-th bit
>>> True
person Zaur Nasibov    schedule 30.03.2012
comment
Спасибо, но это не совсем ответ на мой вопрос. Учитывая байт = 178, как бы вы извлекли флаг == 1 и числовые байты == 50? - person Evan Borgstrom; 30.03.2012
comment
+1: но часть !=0 не нужна, if x&0b10 должно быть достаточно. - person tom10; 30.03.2012
comment
!= 0 не так плохо, как == True, но близко. :) - person Sven Marnach; 30.03.2012
comment
Явное лучше, чем неявное, поэтому x & 0b10 != 0 меня устраивает - person D.Shawley; 30.03.2012
comment
Рассмотрим это: bin(178) == 10110010; Если я правильно понял, вам нужно вернуть все байты, начиная, т.е. с 5-го байта. Тогда 178 & (2 ^ 5 - 1) должно помочь, потому что в этом случае 10110010 & 00011111 == 00010010 - person Zaur Nasibov; 30.03.2012

Если я правильно прочитал ваше описание:

if (byte & 0x80) != 0:
    num_bytes = byte & 0x7F
person D.Shawley    schedule 30.03.2012

ну вот:

class ControlWord(object):
    """Helper class to deal with control words.

    Bit setting and checking methods are implemented.
    """
    def __init__(self, value = 0):
        self.value = int(value)
    def set_bit(self, bit):
        self.value |= bit
    def check_bit(self, bit):
        return self.value & bit != 0
    def clear_bit(self, bit):    
        self.value &= ~bit
person K.-Michael Aye    schedule 30.03.2012

не уверен, что правильно вас понял, но если да, то это должно помочь:

>>> x = 154 #just an example
>>> flag = x >> 1
>>> flag
1
>>> nb = x & 127
>>> nb
26
person Not_a_Golfer    schedule 30.03.2012

Вы можете сделать это следующим образом:

def GetVal(b):
   # mask off the most significant bit, see if it's set
   flag = b & 0x80 == 0x80
   # then look at the lower 7 bits in the byte.
   count = b & 0x7f
   # return a tuple indicating the state of the high bit, and the 
   # remaining integer value without the high bit.
   return (flag, count)

>>> testVal = 50 + 0x80
>>> GetVal(testVal)
(True, 50)
person bgporter    schedule 30.03.2012

Вместо int(bin(byte)[3:], 2) можно просто использовать: int(bin(byte>>1),2)

person theonova    schedule 30.03.2012