从字节中提取位

Extracting bits from bytes

我正在处理压缩数据。该格式包含一个查找 table 和一个长整数数组,每个整数可能包含多个值。包含值的位长度因文件而异。我可以访问这些 longs 作为 bytes :是否有一种简单的方法来访问特定的位/位范围,或者我必须从头开始制作一个? 在标准库中

请注意,当位长度不是 64 的因数时,我可能必须继续到下一个 long 值。


代码需要做什么的理论示例:

尝试 numpy 中的 unpackbits 函数 https://numpy.org/doc/stable/reference/generated/numpy.unpackbits.html

Bits = numpy.unpackbits(Bytes)

这个解决方案怎么样:

unpacked = "{0:b}".format(long_int)
unpacked = "0"*(64-len(unpacked)) + unpacked
int(unpacked[25:30],2)

编辑:不起作用! int 构造函数假定一个带符号的 int,并且没有办法告诉它构造一个 uint

这是我找到的一个 hacky 解决方案。虽然看起来很笨重

def bitValue(byteValue, start, length):
    """Extract length bits from byteValue at start, and return them as an integer"""
    return int(bin(byteValue).lstrip('0b').rjust(64, '0')[start:start+length], 2)

这是一个不依赖于 numpy 等外部库或字符串转换的解决方案:

def get_bits(num, start, end, length=64):
    '''Like bits(num)[from:to] interpreted as int'''
    mask = 2**(end-start)-1
    shift = length - (end-start) - start
    return (num & (mask << shift)) >> shift


print(get_bits(17, 0, 3, length=6))  # 010001[0:3] -> 010 = 2
print(get_bits(17, 3, 6, length=6))  # 010001[3:6] -> 001 = 1
print(get_bits(17, 0, 6, length=6))  # 010001[0:6] -> 010001 = 17
print(get_bits(4503672641818897, 25, 30))  # ...[25:30] -> 00100 = 4

解释:

  • mask = 2**(end-start)-1end-start是到select的位数(N),那么2**N就是一个有N个零的1(2**3 -> 1000)。 2**N - 1那么就是N个(1000 - 1 = 111).
  • shift = length - (end-start) - start:我们希望掩码向左移动的位数 (111 << 3 = 111000) 以及我们希望结果向右移动的位数:010001 & 111000 是 010000,我们只需要前三位。 010000 >> 3 是 010.
  • return (num & (mask << shift)) >> shift: 现在我们把它们放在一起