0

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

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

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

我该如何解决这个错误,究竟是什么失败了?

4

1 回答 1

2

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

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

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

正如文档解释的那样:

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

那么,您如何处理呢?

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

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

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


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


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

这有许多优点。一方面,它使格式易于人类阅读(并且人类可编辑/可调试)。而且,虽然有时会带来空间折衷,但单字符终止符至少与长度前缀格式一样有效,甚至可能更高。而且,最后但同样重要的是,这意味着代码对于生成和解析标头都非常简单。

在稍后的评论中,您澄清说您也想编写整数,但这并没有改变任何东西。一个'i'int 值将占用 4 个字节,但大多数应用程序会写入很多小数字,如果将它们写为字符串,则仅占用 1-2 个字节(终止符/分隔符 +1)。如果你不是在写小数字,Pythonint很容易太大而无法容纳在 Cint中——在这种情况下,struct会默默地溢出,只写低 32 位。

于 2013-02-20T01:34:54.573 回答