如何使用Python从二进制文件解压缩字节数组?

时间:2013-05-12 21:35:28

标签: python python-3.x

我正在给自己一个使用Python读取二进制文件的速成课程。我是两个人的新手,所以请耐心等待。

文件格式的文档告诉我前16个字节是一个GUID,并且进一步阅读告诉我这个GUID的格式是这样的:

typedef struct {
  unsigned long Data1;
  unsigned short Data2;
  unsigned short Data3;
  byte Data4[8];
} GUID, 
 UUID, 
 *PGUID;

到目前为止,我已经能够解压缩结构中的前三个条目,但我对#4感到难过。我认为这是一个8字节的数组,但我不确定如何解压缩它。

import struct

fp = open("./file.bin", mode='rb')

Data1 = struct.unpack('<L', fp.read(4)) # unsigned long, little-endian
Data2 = struct.unpack('<H', fp.read(2)) # unsigned short, little-endian 
Data3 = struct.unpack('<H', fp.read(2)) # unsigned short, little-endian
Data4 = struct.unpack('<s', bytearray(fp.read(8))) # byte array with 8 entries?

struct.error: unpack requires a bytes object of length 1

Data4我做错了什么? (我使用的是Python 3.2 BTW)

Data1到3都可以。如果我对它们使用hex(),我会得到我期望看到的正确数据(哇哇)我只是对这个字节数组的语法失败了。

编辑:回答

我正在阅读MS-DTYP中定义的GUID,并将其钉住:

data = uuid.UUID(bytes_le=fp.read(16))

2 个答案:

答案 0 :(得分:9)

如果你想要一个8字节的字符串,你需要将数字8放在那里:

struct.unpack('<8s', bytearray(fp.read(8)))

来自the docs

  

格式字符可以在整数重复计数之前。例如,格式字符串'4h'表示与'hhhh'完全相同。

...

  

对于's'格式字符,计数被解释为字节的长度,而不是像其他格式字符那样的重复计数;例如,'10s'表示单个10字节字符串,而'10c'表示10个字符。如果未给出计数,则默认为1.对于打包,字符串将被截断或填充为适当的空字节以使其适合。对于解包,生成的字节对象始终具有指定的字节数。作为一种特殊情况,'0s'表示单个空字符串(而'0c'表示0个字符)。


但是,我不确定你为什么要这样做。

fp.read(8)为您提供了一个8字节的bytes对象。你想要一个8字节的bytes对象。所以,就这样做:

Data4 = fp.read(8)

bytes转换为bytearray除了制作可变副本外无效。解压缩它只会让您返回您开始使用的同一bytes的副本。所以......为什么?


嗯,实际上,struct.unpack会返回tuple,其中一个值是您开始使用的bytes的副本,但您可以这样做:

Data4 = (fp.read(8),)

这提出了为什么首先需要四个单元素元组的问题。你将无缘无故地在Data1[0]等所有地方进行。为什么不呢?

Data1, Data2, Data3, Data4 = struct.unpack('<LHH8s', fp.read(16))

当然,如果这是为了阅读UUID,那么使用“包含的电池”总是比尝试用镍和镉矿石制造自己的电池更好。正如icktoofay所说,只需使用uuid模块:

data = uuid.UUID(bytes_le=fp.read(16))

但请记住,Python的uuid使用的是4-2-2-1-1-6格式,而不是4-2-2-8格式。如果你真的需要这种格式,你需要转换它,这意味着无论如何要么struct还是有点笨拙。 (微软的GUID通过使用4-2-2-2-6格式使事情变得更加有趣,这两种格式都不同,并代表原生端的前3个和大端的最后两个,因为它们喜欢让事情变得更容易......)

答案 1 :(得分:3)

Python UUIDs支持

the uuid module。做这样的事情:

import uuid

my_uuid = uuid.UUID(bytes_le=fp.read(16))