在Python中读取二进制文件
我需要在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读取这个数据集。
3 个回答
看起来你想读取一个叫做 cooling_0000x.out 的文件,这个文件是由RAMSES生成的。
要注意的是,文件开头的前两个整数(n1, n2)表示后面会有两个维度的表格(数组)的大小……所以你需要先处理这两个整数,才能知道文件里还有多少实际的数据。
scipy这个库应该能帮上忙——它可以让你读取任意维度的二进制数据:
http://wiki.scipy.org/Cookbook/InputOutput#head-e35c7736718209eea00ebf37a7e1dfb91df696e1
如果你已经有了这个Python代码,请告诉我,因为我今天(2014年9月17日)打算写这个代码。
Rick
你需要的就是 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)