Python二进制文件读取问题

3 投票
2 回答
2500 浏览
提问于 2025-04-16 00:42

我正在尝试在Python中读取一个二进制文件,这个文件表示一个Matlab中的矩阵。不过,我在读取这个文件并把字节转换成正确的数值时遇到了麻烦。

这个二进制文件由一系列4字节的数字组成。前两个数字分别是行数和列数。我的朋友给了我一个他写的Matlab函数,使用fwrite来处理这个问题。我想在Python中做类似的事情:

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'

所以前4个字节和第5到第8个字节都应该是4,因为这个矩阵是4x4的。接下来应该是4,4,2,4,2,2,2,1等等……

谢谢大家!

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的用法。

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)

撰写回答