我正在给自己一个使用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))
答案 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)