用struct编写和读取头文件

时间:2013-02-20 01:29:00

标签: python struct

我有一个文件标题,我正在阅读并计划编写,其中包含有关内容的信息;版本信息和其他字符串值。

写入文件并不太难,看起来非常简单:

outfile.write(struct.pack('<s', "myapp-0.0.1"))

但是,当我尝试用另一种方法从文件中读回标题时:

header_version = struct.unpack('<s', infile.read(struct.calcsize('s')))

我抛出了以下错误:

struct.error: unpack requires a string argument of length 2

如何修复此错误以及究竟是什么失败?

1 个答案:

答案 0 :(得分:2)

  

写入文件并不太难,看起来非常简单:

不像你想象的那么简单。尝试查看文件中的内容,或者只打印出您正在撰写的内容:

>>> struct.pack('<s', 'myapp-0.0.1')
'm'

正如the docs解释:

  

对于's'格式字符,计数被解释为字符串的大小,而不是其他格式字符的重复计数;例如,'10s'表示单个10字节字符串,而'10c'表示10个字符。 如果未给出计数,则默认为1。

那么,你怎么处理这个?

  1. 如果不是您想要的话,请不要使用struct。使用struct的主要原因是与C代码进行交互,C代码直接将C struct对象转储到缓冲区/文件/套接字/任何内容,或者以类似样式编写的二进制格式规范(例如, IP标头)。它不适用于Python数据的一般序列化。正如Jon Clements在评论中指出的那样,如果您要存储的只是一个字符串,那么只需要write字符串。如果您想存储更复杂的内容,请考虑json模块;如果您想要更灵活,更强大的内容,请使用pickle

  2. 使用固定长度的字符串。如果文件格式规范的一部分是名称必须始终不超过255个字符,则只需编写'<255s'即可。较短的字符串将被填充,较长的字符串将被截断(您可能需要检查它以引发异常而不是静默截断)。

  3. 使用一些带内或带外方式传递长度。最常见的是长度前缀。 (您可以使用'p''P'格式来提供帮助,但这实际上取决于您尝试匹配的C布局/二进制格式;通常您必须做一些丑陋的事情比如struct.pack('<h{}s'.format(len(name)), len(name), name)。)


  4. 至于您的代码失败的原因,有多种原因。首先,read(11)不能保证读取11个字符。如果文件中只有1个字符,那么您将获得所有字符。其次,您实际上并没有打电话read(11),而是在调用read(1),因为struct.calcsize('s')会返回1(原因应该从上面显而易见)。第三,要么您的代码与您上面显示的完全不同,要么infile的文件指针不在正确的位置,因为编写的代码会成功读入字符串'm'并将其解压缩为'm'。 (我在这里假设Python 2.x; 3.x会遇到更多问题,但你甚至不会那么做。)


    对于您的特定用例(&#34;文件头...包含有关内容的信息;版本信息和其他字符串值&#34;),我只需使用write字符串换行终结者。 (如果字符串可以嵌入换行符,则可以反斜杠 - 将它们转义为\n,使用C风格或RFC822样式的延续,引用它们等。)

    这有许多优点。首先,它使格式具有人类可读性(人类可编辑/可修改)。而且,虽然有时会带来空间权衡,但单字符终结符至少与长度前缀格式一样有效,可能更有效。而且,最后但并非最不重要的是,这意味着代码对于生成和解析标头都很简单。

    在稍后的评论中,您澄清了您还想要编写注释,但这并不会改变任何内容。 'i' int值将占用4个字节,但大多数应用程序会写入许多小数字,如果将它们写为字符串,则只需要1-2个字节(终结符/分隔符为+1)。如果您不是在写小数字,那么Python int很容易变得太大而无法容纳在C int中 - 在这种情况下struct会静默溢出并只写低32位。