Numpy 用Python读取Fortran二进制文件

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中成

在Python中读取未格式化的F77二进制文件时遇到问题。 我尝试了
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']