用Python读取二进制文件

4

我需要在Python中读取一个二进制文件。这个文件最初是由Fortran 90程序以如下方式编写的:

open(unit=10,file=filename,form='unformatted')
write(10)table%n1,table%n2
write(10)table%nH
write(10)table%T2
write(10)table%cool
write(10)table%heat
write(10)table%cool_com
write(10)table%heat_com
write(10)table%metal
write(10)table%cool_prime
write(10)table%heat_prime
write(10)table%cool_com_prime
write(10)table%heat_com_prime
write(10)table%metal_prime
write(10)table%mu
if (if_species_abundances) write(10)table%n_spec
close(10)

我可以用以下IDL代码轻松读取这个二进制文件:
n1=161L
n2=101L
openr,1,file,/f77_unformatted
readu,1,n1,n2
print,n1,n2
spec=dblarr(n1,n2,6)
metal=dblarr(n1,n2)
cool=dblarr(n1,n2)
heat=dblarr(n1,n2)
metal_prime=dblarr(n1,n2)
cool_prime=dblarr(n1,n2)
heat_prime=dblarr(n1,n2)
mu  =dblarr(n1,n2)
n   =dblarr(n1)
T   =dblarr(n2)
Teq =dblarr(n1)
readu,1,n
readu,1,T
readu,1,Teq
readu,1,cool
readu,1,heat
readu,1,metal
readu,1,cool_prime
readu,1,heat_prime
readu,1,metal_prime
readu,1,mu
readu,1,spec
print,spec
close,1

我想做的是用Python读取这个二进制文件。但是有些问题。 首先,这是我读取文件的尝试:
import numpy
from numpy import *
import struct

file='name_of_my_file'
with open(file,mode='rb') as lines:
    c=lines.read()

我尝试读取前两个变量:

dummy, n1, n2, dummy = struct.unpack('iiii',c[:16])

但是您可以看到,我不得不添加虚拟变量,因为一些Fortran程序在这些位置上添加了整数8。

现在的问题是尝试读取其他字节时,我没有得到与IDL程序相同的结果。

这是我尝试读取数组n的方法:

 double = 8
 end = 16+n1*double
 nH = struct.unpack('d'*n1,c[16:end])

然而,当我打印这个数组时,我得到了无意义的值。我的意思是,我可以用上面的IDL代码读取文件,所以我知道该期望什么。那么我的问题是:在不知道确切结构的情况下,如何读取此文件?为什么使用IDL可以如此简单地读取它?我需要使用Python读取此数据集。


展示你文件中的前两三行。另外,你在顶部设置的变量与从文件中读取“n”没有太大关系... - Daren Thomas
但我的文件是二进制文件。这就是为什么我向您展示了在IDL中读取它的代码...最终我想要读取我声明的所有变量,而不仅仅是n。 - Brian
4
快速搜索显示,“IDLSave - 一个用于读取IDL 'save'文件的Python模块”http://astrofrog.github.com/idlsave/ - georg
当我不知道文件的确切结构时,我该如何读取它?哪一部分是未知的?那四个整数不总是存在吗?数据不总是双精度吗? - gecco
@georg,“save”文件和“.bil”文件是一样的吗? - eduardosufan
3个回答

6
你需要的是 struct 模块。
该模块允许你从字符串中解包数据,将其视为二进制数据。
你提供一个格式化字符串和文件字符串,它会消耗数据并将其返回给你作为二进制对象。
例如,使用你的变量:
import struct
content = f.read() #I'm not sure why in a binary file you were using "readlines",
                   #but if this is too much data, you can supply a size to read()
n, T, Teq, cool = struct.unpack("dddd",content[:32])

这将使n、T、Teq和cool保存您的二进制文件中的前四个浮点数。当然,这只是一个演示。您的示例似乎想要双精度浮点数列表 - 方便的 struct.unpack 返回一个元组,我认为对于您的情况仍然可以正常工作(如果不行,您可以将其转换为列表)。请注意,struct.unpack需要消耗传递给它的整个字符串 - 否则您将得到struct.error。因此,要么切片您的输入字符串,要么仅读取您将使用的字符数,就像我上面在评论中说的那样。
例如,
n_content = f.read(8*number_of_ns) #8, because doubles are 8 bytes
n = struct.unpack("d"*number_of_ns,n_content)

0
你尝试过使用scipy.io.readsav吗?
只需像这样简单地读取文件:
mydict = scipy.io.readsav('name_of_file')

好的,那就忘记这个答案吧。我建议你更新你的帖子,澄清你所谈论的是二进制文件,而不是保存文件,并且你不能在评论中使用模块 IDL save 的引用。 - gecco

0

看起来你正在尝试读取由RAMSES生成的cooling_0000x.out文件。

请注意,前两个整数(n1、n2)提供了在文件主体中跟随的二维表格(数组)的维度……因此,在你了解其余文件中有多少实际*8数据之前,你需要先处理这两个整数。

scipy可能会有所帮助——它可以让你读取任意维度的二进制数据:

http://wiki.scipy.org/Cookbook/InputOutput#head-e35c7736718209eea00ebf37a7e1dfb91df696e1

如果您已经有这个Python代码,请告诉我,因为我本来今天(2014年9月17日)要写它的。
Rick

scipy.io.FortranFile() 允许您读取Fortran格式的非格式化文件。(此外,RAMSES提供了一个名为 fortranfile.py 的文件) - mivkov

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