用Python读取Fortran二进制文件

时间:2018-12-05 19:00:22

标签: numpy io scipy fortran binaryfiles

我无法在Python中读取未格式化的F77二进制文件。 我尝试了SciPy.io.FortraFile方法和NumPy.fromfile方法,但都无济于事。我还阅读了IDL中的文件,该文件可以正常工作,因此我对数据的外观有一个基准。我希望有人能指出我这一个愚蠢的错误-没有什么比白痴的时刻然后洗手要好了...

数据bcube1的尺寸为101x101x101x3,为r * 8类型。共有3090903个条目。它们是使用以下语句编写的(不是我的代码,是从源代码复制的)。

open (unit=21, file=bendnm, status='new'
.     ,form='unformatted')
write (21) bcube1
close (unit=21)

我可以使用以下命令(同样不是我的代码,从同事那里复制)在IDL中成功读取它:

bcube=dblarr(101,101,101,3)
openr,lun,'bcube.0000000',/get_lun,/f77_unformatted,/swap_if_little_endian
readu,lun,bcube
free_lun,lun

返回的数据(bcube)具有双精度,尺寸为101x101x101x3,因此文件的标题信息知道其尺寸(不是展平的)。

现在,我尝试使用Python获得相同的效果,但是没有运气。我尝试了以下方法。

In [30]: f = scipy.io.FortranFile('bcube.0000000', header_dtype='uint32')
In [31]: b = f.read_record(dtype='float64')

,它返回错误Size obtained (3092150529) is not a multiple of the dtypes given (8)。更改dtype会更改获得的大小,但是它仍然可以被8整除。

或者,使用fromfile不会导致任何错误,但会返回数组中的另一个值(也许是页脚?),并且各个数组的值都非常错误(应该全部为1)。

In [38]: f = np.fromfile('bcube.0000000')
In [39]: f.shape
Out[39]: (3090904,)
In [42]: f
Out[42]: array([ -3.09179121e-030,   4.97284231e-020,  -1.06514594e+299, ...,
         8.97359707e-029,   6.79921640e-316,  -1.79102266e-037])

我尝试使用byteswap来查看这是否使浮点值更合理,但事实并非如此。

在我看来,np.fromfile方法非常有效,但是读取标头信息的方式一定存在问题。谁能建议我如何弄清楚头文件中应该包含哪些 ,以便IDL知道数组的维数和数据类型?有没有一种方法可以将标头信息传递给fromfile,以便它知道如何处理开头的条目?

2 个答案:

答案 0 :(得分:1)

我玩了一点,我想我有个主意。

Fortran如何存储未格式化的数据尚未标准化,因此您必须对其进行一些尝试,但是您需要三项信息:

  1. 数据格式。您建议这是64位实数,或者在python中为'f8'。
  2. 标头的类型。这是一个无符号整数,但是您需要以字节为单位的长度。如果不确定,请尝试4。

    标头通常以字节为单位存储记录的长度,并在末尾重复。

    再说一次,它不是标准化的,所以没有保证。

  3. 字节序,大小不一。

    从技术上讲,标头和值均适用,但我认为它们是相同的。

    Python默认为little endian,因此,如果这是您数据的正确设置,我想您已经解决了。

使用scipy.io.FortranFile打开文件时,需要提供 header 的数据类型。因此,如果数据存储为big_endian,并且您有一个4字节的无符号整数标头,则需要这样做:

from scipy.io import FortranFile
ff = FortranFile('data.dat', 'r', '>u4')

读取数据时,需要值的数据类型。同样,假设为big_endian,则您需要输入>f8

vals = ff.read_reals('>f8')

here中查找有关数据类型的语法的描述。

如果您对写入数据的程序有控制权,强烈建议您将它们写入数据流,而Python可以更轻松地读取它们。

答案 1 :(得分:0)

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 相关的实用程序,我还没有完全探索。