从文件中每 4 个字节读取 3 个字节到 bytearray

Read 3 of every 4 bytes from file into bytearray

我是 python 的新手。我目前正在将文件的内容读入字节数组,如下所示:

self.palette = bytearray(fp.read(paletteSize*4))

我不需要读取所有数据,而是需要执行以下操作:

palette[0] = fp.read(1)
palette[1] = fp.read(1)
palette[2] = fp.read(1)
fp.read(1) #throw a byte away
palette[3] = fp.read(1)
# etc thru paletteSize * 4 bytes, resulting in a size * 3 array

基本上,我需要丢弃每 4 个字节并将剩余的字节打包到一个大小为 3/4 的数组中。

执行此操作的最佳方法是什么,同时代码性能最佳且最干净?我知道我可以像上面那样在范围内使用 for 循环手动执行此操作,但这看起来真的很难看,并且在 python 而不是本机 C 代码中涉及大量内存副本。

我问的原因:这是微型 python 文件可能有点大 (4+ KB),因此节省 25% 很重要。

剧透警报:我正在读取位图文件的调色板部分,它被编码为蓝色、绿色、红色、0x00 的四个字节元组 - 每个调色板条目的第 4 个字节是无用数据。

由于您已经有了调色板大小,您可以为它预分配内存,以节省一些在向字节数组添加数据时扩展字节数组的开销。使用循环一次读取 3 个字节并执行 seek 以跳过 1 个字节:

palette = bytearray(paletteSize * 3)
for i in range(paletteSize):
    palette[i * 3: (i + 1) * 3] = fp.read(3)
    fp.seek(1, 1)

使用struct模块。

s = struct.Struct("BBB")
b = bytearray()
for i in range(paletteSize)
    b.extend(s.unpack_from(f.read(4)))  # b.extend(f.read(4)[:3]) would also work

您甚至可以将其简化为

s = struct.Struct("BBBx" * paletteSize)
b = bytearray(s.unpack(f.read(paletteSize*4)))

虽然我还没有测试过这个;我从来没有使用过这么大的格式字符串,但理论上它应该可以工作。

所以今天我学习了Python中的切片赋值,这是缺失的魔法。谢谢@blhsing :)

我最终得到了这个版本,它避免了循环切片赋值中的一些数学运算:

self.palette = bytearray(paletteSize * 3)
   for i in range(0, paletteSize*3, 3):
      self.palette[i : (i + 3)] = fp.read(3)
      fp.seek(1, 1)

添加为答案而不是评论,因为 Whosebug 评论格式很糟糕