从文件中提取位(子字节)数字的最快方法

5
给定一个具有分辨率压缩二进制数据的文件,我希望在Python中将子字节位转换为它们的整数表示。我的意思是需要将文件中的n个位解释为一个整数。
目前,我正在将文件读入bitarray对象,并将对象的子集转换为整数。这个过程虽然可行,但速度较慢且繁琐。是否有更好的方法来做到这一点,例如使用struct模块?
import bitarray

bits = bitarray.bitarray()
with open('/dir/to/any/file.dat','r') as f:
    bits.fromfile(f,2) # read 2 bytes into the bitarray

    ## bits 0:4 represent a field
    field1 = int(bits[0:4].to01(), 2)  # Converts to a string of 0s and 1s, then int()s the string

    ## bits 5:7 represent a field
    field2 = int(bits[4:7].to01(), 2)

    ## bits 8:16 represent a field
    field3 = int(bits[7:16].to01(), 2)

print """All bits: {bits}\n\tfield1: {b1}={field1}\n\tfield2: {b2}={field2}\n\tfield3: {b3}={field3}""".format(
        bits=bits, b1=bits[0:4].to01(), field1=field1, 
        b2=bits[4:7].to01(), field2=field2,
        b3=bits[7:16].to01(), field3=field3)

输出:

All bits: bitarray('0000100110000000')
    field1: 0000=0
    field2: 100=4
    field3: 110000000=384

在文件中,字段大小是否会重复出现,还是完全随机的?它们是否跨越字节边界? - Mark Ransom
  1. 字段跨越字节边界。
  2. 字段会重复,但存在一个未知大小的块,必须动态读取。例如,在我的示例中,可以说 field3 告诉我 field4 需要多大空间。然后文件会重复 field1field2 等。
- zachd1_618
field4的大小是以字节还是位为单位?它必须全部适合一个整数吗? - SoreDakeNoKoto
field4 是以字节为单位的。 - zachd1_618
2个回答

4
这应该适用于您的特定情况:
#bitmasks of fields 1-3, they fit in 2 bytes
FIELD1 = 0b1111000000000000 # first 4 bits
FIELD2 = 0b0000111000000000 # next 3 bits
FIELD3 = 0b0000000111111111 # last 9 bits

def bytes_to_int(num):  #convert bytes object to an int
    res = 0
    num = num[::-1]  # reverse the bytes
    for i in range(len(num)):
        res += num[i] * (256**i)
    return res

def get_fields(f):
    chunk = bytes_to_int(f.read(2))  # read 2 bytes, f1-f3, convert to int
    f1 = (chunk & FIELD1) >> 12  # get each field with its bitmask
    f2 = (chunk & FIELD2) >> 9
    f3 = chunk & FIELD3
    f4 = f.read(f3)  # field4 as a bytes object

    return f1, f2, f3, f4

file = open('file.dat','rb')

#using your sample data
print(get_fields(file))  # returns 0, 4, 384, field4 as a bytes obj

file.close()

很好的提及int.from_bytes。不幸的是,我正在运行2.7版本。但这正是我正在寻找的。 - zachd1_618
@zachd1_618 好的...我可以纠正那个...你是以文本还是原始字节格式读取文件?如果只是作为文本的1和0,则int(n,2)可以进行转换。此外,是否正确地说,由于field4是以字节为单位且不跨越字节边界,因此仅字段1-3处于子字节级别,并且它们都适合于2个字节中? - SoreDakeNoKoto
我正在以原始字节读取文件。就像你的例子中使用 file.read(num_bytes) 一样。是的,可以正确地说未知大小的 field4 是整个字节,并且不跨越字节边界。 - zachd1_618
@zachd1_618 这个应该适用于你在Python 2中的特定情况。 - SoreDakeNoKoto
很棒的表述!按位运算符是我所缺少的。我为我的场景(必须动态构建“FIELD”掩码)调整了该方法,并获得了比使用“bitarray”对象提高了一个数量级的性能。非常好。 - zachd1_618

4
如果你愿意使用别人的模块,bitstring模块似乎可以很好地表示和操作位:http://pythonhosted.org/bitstring/index.html 例如,如果你知道字段的大小,你可以使用格式字符串:http://pythonhosted.org/bitstring/reading.html#reading-using-format-strings
import bitstring
bitstream = bitstring.ConstBitStream(filename='testfile.bin')
field1, field2, field3 = bitstream.readlist('int:4, int:3, int:9')

如果您不知道字段大小,可以先读入所有位,然后使用切片来提取所有字段:http://pythonhosted.org/bitstring/slicing.html
import bitstring
bitstream = bitstring.ConstBitStream(filename='testfile.bin')
bits = bitstring.BitArray(bitstream)
field1 = bits[0:4].int
field2 = bits[4:7].int
field3 = bits[7:16].int

只是一个想法,您可能已经找到了这个模块。


哦,如果你想使用无符号而不是有符号,请使用uint。 - pmartin

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