从字节中提取位
Extracting bits from bytes
我正在处理压缩数据。该格式包含一个查找 table 和一个长整数数组,每个整数可能包含多个值。包含值的位长度因文件而异。我可以访问这些 longs 作为 bytes
:是否有一种简单的方法来访问特定的位/位范围,或者我必须从头开始制作一个? 在标准库中
请注意,当位长度不是 64 的因数时,我可能必须继续到下一个 long 值。
代码需要做什么的理论示例:
- 取长整数
4503672641818897L
- 将其转换为位(应该 return
0000000000010000000000000001000100000000000000000001000100010001
)
- 读取查找 table 并确定值的长度(假设这次是
5
位)
- 读取第 6 个值,位 25 - 29 (
00100
)
- Return 整数值
4
尝试 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)-1
:end-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
: 现在我们把它们放在一起
我正在处理压缩数据。该格式包含一个查找 table 和一个长整数数组,每个整数可能包含多个值。包含值的位长度因文件而异。我可以访问这些 longs 作为 bytes
:是否有一种简单的方法来访问特定的位/位范围,或者我必须从头开始制作一个? 在标准库中
请注意,当位长度不是 64 的因数时,我可能必须继续到下一个 long 值。
代码需要做什么的理论示例:
- 取长整数
4503672641818897L
- 将其转换为位(应该 return
0000000000010000000000000001000100000000000000000001000100010001
) - 读取查找 table 并确定值的长度(假设这次是
5
位) - 读取第 6 个值,位 25 - 29 (
00100
) - Return 整数值
4
尝试 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)-1
:end-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
: 现在我们把它们放在一起