Numpy 用Python读取Fortran二进制文件
在Python中读取未格式化的F77二进制文件时遇到问题。 我尝试了Numpy 用Python读取Fortran二进制文件,numpy,io,scipy,fortran,binaryfiles,Numpy,Io,Scipy,Fortran,Binaryfiles,在Python中读取未格式化的F77二进制文件时遇到问题。 我尝试了SciPy.io.FortraFile方法和NumPy.fromfile方法,但均无效。我还阅读了IDL中的文件,它是有效的,所以我有一个数据应该是什么样的基准。我希望有人能指出我犯的一个愚蠢的错误——没有什么比有一个愚蠢的时刻然后洗手不干更好的了 数据bcube1的尺寸为101x101x101x3,为r*8型。总共有3090903个条目。它们是使用以下语句编写的(不是我的代码,是从源代码复制的) 我可以使用以下代码在IDL中成
SciPy.io.FortraFile
方法和NumPy.fromfile
方法,但均无效。我还阅读了IDL中的文件,它是有效的,所以我有一个数据应该是什么样的基准。我希望有人能指出我犯的一个愚蠢的错误——没有什么比有一个愚蠢的时刻然后洗手不干更好的了
数据bcube1的尺寸为101x101x101x3,为r*8型。总共有3090903个条目。它们是使用以下语句编写的(不是我的代码,是从源代码复制的)
我可以使用以下代码在IDL中成功读取它(也不是我的代码,是从同事那里复制的):
返回的数据(bcube)是双精度的,尺寸为101x101x101x3,因此文件的头信息知道其尺寸(未展平)
现在我尝试使用Python获得相同的效果,但运气不佳。我试过以下方法
In [30]: f = scipy.io.FortranFile('bcube.0000000', header_dtype='uint32')
In [31]: b = f.read_record(dtype='float64')
返回错误获取的大小(3092150529)不是给定数据类型(8)的倍数。更改数据类型会更改获得的大小,但它仍然不可分割8
或者,使用fromfile
不会导致任何错误,但会返回数组中的一个或多个值(可能是一个页脚?),并且单个数组值错误很大(应该是顺序统一的)
我尝试过使用byteswap,看看这是否会使浮点值更合理,但事实并非如此
在我看来,np.fromfile
方法非常接近工作状态,但它读取头信息的方式肯定有问题。有谁能建议我如何确定头文件中应该包含哪些内容,以允许IDL了解数组维度和数据类型?是否有一种方法可以将头信息从文件中传递到,以便它知道如何处理前导条目?我对它进行了一些研究,我想我有一个想法
Fortran存储未格式化数据的方式尚未标准化,因此您必须对其进行一些处理,但您需要三条信息:
数据的格式。您建议使用64位实数,或者python中的“f8”
标题的类型。这是一个无符号整数,但需要以字节为单位的长度。如果不确定,请尝试4
标头通常以字节为单位存储记录的长度,并在末尾重复
再说一次,它是不标准的,所以没有保证
endianness,无论大小
从技术上讲,标题和值都是相同的,但我假设它们是相同的
Python默认为little endian,因此如果这是数据的正确设置,我认为您已经解决了它
当您使用scipy.io.FortranFile
打开文件时,需要给出头的数据类型。因此,如果数据存储为big_endian,并且有一个4字节无符号整数头,则需要:
from scipy.io import FortranFile
ff = FortranFile('data.dat', 'r', '>u4')
读取数据时,需要值的数据类型。同样,假设是big_endian,您需要键入>f8
:
vals = ff.read_reals('>f8')
查找数据类型语法的描述
如果您可以控制写入数据的程序,我强烈建议您将它们写入数据流,Python可以更容易地读取这些数据流。Fortran有记录界限,即使在二进制文件中也没有很好的文档记录
因此,每次写入未格式化文件时:
integer*4 Test1
real*4 Matrix(3,3)
open(78,format='unformatted')
write(78) Test1
write(78) Matrix
close(78)
最终应使用np.int32值填充。(我看到过一些参考资料,它们告诉您记录长度,但还没有亲自验证。)
以上内容可以通过numpy在Python中读取为:
input_file = open(file_location,'rb')
datum = np.dtype([('P1',np.int32),('Test1',np.int32),('P2',np.int32),('P3',mp.int32),('MatrixT',(np.float32,(3,3))),('P4',np.int32)])
data = np.fromfile(input_file,datum)
应使用上述格式的单个数据集完全填充数据数组。请注意,numpy希望数据以C格式(行主格式)打包,而Fortran格式的数据以列主格式打包。对于上面这样的方形矩阵形状,这意味着在使用之前,从矩阵中获取数据也需要转置。对于非方形矩阵,您需要重塑和转置:
Matrix = np.transpose(data[0]['MatrixT']
转换您的4-D数据结构需要小心地进行。您可以在SciPy中寻找实现自动化的方法;SciPy软件包似乎有与Fortran相关的实用程序,我还没有完全研究过。您有没有看过例如(由google找到;python读取Fortran二进制文件)请使用tag来回答所有Fortran问题。你的问题不是特定版本的。@VladimirF如果我的问题不清楚,我很抱歉。也许我可以换个说法。为什么np.fromfile(fname)
返回的值比数组中的值多?在可能的情况下,应该有3090903个条目,但结果有3090904个条目。为什么它返回的值与源数组中的值不相等?@albert是的,我已经看过那篇文章了。它解决了数组内容的数据类型错误的问题。但是,我知道数组中的数据是r8,所以我知道python数据类型应该是float64。不用担心。我只是指的是fortran标签和fortran标签77,而不是你问题的清晰性。这就解决了它!非常感谢你!对于未来的读者,解决方案是使用数组接口定义的数据类型。TLDR;'>u4'和'>f8'表示bigendian'uint32'和'float64',但特定于C/F API。
integer*4 Test1
real*4 Matrix(3,3)
open(78,format='unformatted')
write(78) Test1
write(78) Matrix
close(78)
input_file = open(file_location,'rb')
datum = np.dtype([('P1',np.int32),('Test1',np.int32),('P2',np.int32),('P3',mp.int32),('MatrixT',(np.float32,(3,3))),('P4',np.int32)])
data = np.fromfile(input_file,datum)
Matrix = np.transpose(data[0]['MatrixT']