Python:从字节中提取位

33
我正在用 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的计算?

你会如何处理这个问题?


7个回答

27

检查比特位是否被设置的经典方法是使用二进制 "and" 运算符,即

x = 10 # 1010 in binary
if x & 0b10:  # explicitly: x & 0b0010 != 0
    print('First bit is set')
为了检查第n位是否设置,可以使用2的幂次方,或更好的位移操作。
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

谢谢,但那并没有真正回答我的问题。假设byte=178,你如何提取 flag==1 和 numbytes==50? - Evan Borgstrom
1
+1:但是不需要!=0这部分,if x&0b10就足够了。 - tom10
2
!= 0并不像== True那么糟糕,但它也很接近。 :) - Sven Marnach
3
“显式优于隐式”因此 x & 0b10 != 0 对我来说可以。 - D.Shawley
考虑这个问题:bin(178) == 10110010;如果我理解正确,您需要返回所有字节,从第5个字节开始。那么,178 & (2^5 - 1) 就可以解决问题了,因为在这种情况下,10110010 & 00011111 == 00010010。 - Zaur Nasibov
1
打印('第一位已设置')- 这里的“第一”这个词非常令人困惑。 - Wolf

18

您可以通过将掩码与来自文件的字节进行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

谢谢。在这种情况下,我也更喜欢二进制数字。 - Evan Borgstrom
3
在Python 3中,您可以编写:data = b'\xaa\x55\x00\x80'for byte in data: ...。不要使用bytes名称,它是一个内置类型。 - jfs

1

你可以使用 int(bin(byte>>1),2) 替代 int(bin(byte)[3:], 2)


1
如果我正确理解了您的描述:
if (byte & 0x80) != 0:
    num_bytes = byte & 0x7F

1

请看:

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

0

不确定我是否理解您的意思,但如果是的话,这应该可以解决问题:

>>> x = 154 #just an example
>>> flag = x >> 1
>>> flag
1
>>> nb = x & 127
>>> nb
26

0
你可以像这样做:
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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接