Python二进制文件读取问题

3
我正在尝试在Python中读取一个二进制文件(该文件表示Matlab中的矩阵)。但是我在读取文件并将字节转换为正确值方面遇到了麻烦。
该二进制文件由一系列4字节数字组成。前两个数字分别是行数和列数。我的朋友给了我一个他写的Matlab函数,使用fwrite完成此操作。我想做类似这样的事情:
f = open(filename, 'rb')
rows = f.read(4)
cols = f.read(4)
m = [[0 for c in cols] for r in rows]
r = c = 0
while True:
    if c == cols:
        r += 1
        c = 0
    num = f.read(4)
    if num:
        m[r][c] = num
        c += 1
    else:
        break

但是当我使用f.read(4)时,我得到像'\ x00 \ x00 \ x00 \ x04'这样的东西(这个特定的示例应该表示一个4),我无法弄清楚如何将其转换为正确的数字(使用int,hex或类似的任何内容都不起作用)。我偶然发现了struct.unpack,但那似乎没有什么帮助。

这是一个示例矩阵和相应的二进制文件(当我使用python函数f.read()读取整个文件而没有任何大小参数时):Matlab函数为其创建:

4     4     2     4
2     2     2     1
3     3     2     4
2     2     6     2

'\x00\x00\x00\x04\x00\x00\x00\x04@\x80\x00\x00@\x00\x00\x00@@\x00\x00@\x00\x00\x00@\x80\x00\x00@\x00\x00\x00@@\x00\x00@\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\xc0\x00\x00@\x80\x00\x00?\x80\x00\x00@\x80\x00\x00@\x00\x00\x00'

因为矩阵是4x4的,所以前4个字节和第5-8个字节都应该是4。然后应该是4,4,2,4,2,2,2,1等等...

谢谢大家!


struct 模块是你的好朋友。可能需要一点时间来适应它,但它是一个非常强大的工具。 - Nick Bastin
2个回答

7
rows = f.read(4)
cols = f.read(4)

这两个名称现在都绑定到4字节的字符串。为了将它们转换为整数,

import struct

rowsandcols = f.read(8)
rows, cols = struct.unpack('=ii', rowsandcols)

请查看 文档 了解关于 struct.unpack 的信息。

这对我没有起作用 =/
import struct f = open('Z:\summer reu 2010\m.dat','rb') rowsandcols = f.read(8) rows, cols = struct.unpack('=ii',rowsandcols) rows 67108864 cols 67108864
行和列应该都是4。
- Daniel Waltrip
我无法格式化我的评论。这是一张截图:http://i47.tinypic.com/14ub18n.jpg - Daniel Waltrip
2
考虑到数据被描述为大端序,并且今天大多数流行的 CPU 都是小端序,也许应该使用 !> 而不是 = - Nas Banov
是的,那个可行 Nas。有人能解释一下所有这些不同格式实际上意味着什么吗?大端/小端和本地/标准是什么? - Daniel Waltrip
我在维基百科上查了“字节序”,很抱歉打扰了大家。非常感谢你们的帮助!=) - Daniel Waltrip

2

我看了一下你的问题,因为我以前从未使用过struct,所以这对我来说是一个好的学习活动。结果有几个要点需要注意-首先,数字不是作为4字节整数存储的,而是以大端形式作为4字节浮点数存储的。其次,如果你的示例是正确的,那么矩阵没有按行存储,而是按列存储。例如,输出的方式如下(伪代码):

for j in cols:
  for i in rows:
    write Aij to file

所以在读取后我需要转置结果。以下是给出示例代码:

import struct 

def readMatrix(f):
    rows, cols = struct.unpack('>ii',f.read(8))
    m = [ list(struct.unpack('>%df' % rows, f.read(4*rows)))
             for c in range(cols)
        ]
    # transpose result to return
    return zip(*m)

以下是我们的测试:

>>> from StringIO import StringIO
>>> f = StringIO('\x00\x00\x00\x04\x00\x00\x00\x04@\x80\x00\x00@\x00\x00\x00@@\x00\x00@\x00\x00\x00@\x80\x00\x00@\x00\x00\x00@@\x00\x00@\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\xc0\x00\x00@\x80\x00\x00?\x80\x00\x00@\x80\x00\x00@\x00\x00\x00')
>>> mat = readMatrix(f)
>>> for row in mat:
...     print row
...     
(4.0, 4.0, 2.0, 4.0)
(2.0, 2.0, 2.0, 1.0)
(3.0, 3.0, 2.0, 4.0)
(2.0, 2.0, 6.0, 2.0)

你的回答更好,我道歉。但是,我不知道是否只是我的机器问题,但我必须使用“!”而不是“>”来进行struct.unpack。 - Daniel Waltrip
@Daniel: 嗯,如果'!'和'>'给你不同的结果,那么这很奇怪,我觉得它们应该是相同的。文档说 形式"!" [网络顺序=大端]可用于那些声称他们无法记住网络字节顺序是大端还是小端的可怜之人[" > "]。但如果它能正常工作,就不要动它 - 它没有出问题 :) - Nas Banov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接