【问题标题】:Reading A Binary File In Fortran That Was Created By A Python Code在 Fortran 中读取由 Python 代码创建的二进制文件
【发布时间】:2016-01-26 05:58:28
【问题描述】:

我有一个使用 Python 代码创建的二进制文件。这段代码主要是编写一堆任务来预处理一组数据文件。我现在想在 Fortran 中读取这个二进制文件。二进制文件的内容是简单格式的点坐标,例如:点数,x0, y0, z0, x1, y1, z1, ....

这些二进制文件是使用 numpy 中的“tofile”函数创建的。到目前为止,我在 Fortran 中有以下代码:

integer:: intValue
double precision:: dblValue
integer:: counter
integer:: check
open(unit=10, file='file.bin', form='unformatted', status='old', access='stream')

counter = 1

do 

  if ( counter == 1 ) then
    read(unit=10, iostat=check) intValue
    if ( check < 0 ) then
      print*,"End Of File"
      stop
    else if ( check > 0 ) then
      print*, "Error Detected"
      stop
    else if ( check == 0 ) then
      counter = counter + 1
      print*, intValue
    end if
  else if ( counter > 1 ) then
    read(unit=10, iostat=check) dblValue
    if ( check < 0 ) then
      print*,"End Of File"
      stop
    else if ( check > 0 ) then
      print*, "Error Detected"
      stop
    else if ( check == 0 ) then
      counter = counter + 1
      print*,dblValue
    end if
  end if

end do

close(unit=10)

不幸的是,这不起作用,我得到了垃圾号码(例如 6.4731191026611484E+212、2.2844499004808491E-279 等)。有人可以就如何正确执行此操作提供一些指示吗? 还有什么是在 Python 和 Fortran 之间可互换地编写和读取二进制文件的好方法——因为这似乎将成为我的应用程序的要求之一。

谢谢

【问题讨论】:

  • 我添加了一个很好的详细答案,告诉你使用access=stream:) 我刚刚意识到你已经这样做了,所以我现在删除了我的答案。所以,问题:你确定你的 python int 和你的 fortran integer 的字节大小是一样的吗?你应该检查两者。如果有单个字节的差异,数据的错位会导致reading 之后的垃圾。你用的是什么fortran编译器?你如何声明你的integers?你的 python ints 的具体类型是什么?
  • 如果您真的很绝望,您可以尝试使用 fortran 和 python 生成(假设)相同的虚拟二进制文件,然后查看这两个文件的十六进制转储以查看发生了什么。此外,我之前关于integer 大小的问题显然也适用于所涉及的doubles。即使类型检查出来,如果您在两台非常不同的机器上使用这两个代码,仍然可能存在字节顺序问题。
  • 关于互换性的问题:我宁愿把点数之类的元数据放到一个单独的ASCII头文件中,方便阅读,只把同类数据放到一个二进制文件中,这也允许相当容易的字节序转换。
  • 为了便携性和可互换性,我会使用 HDF5 来读取/写入数据。支持 python 和 Fortran 以及许多其他。
  • 种类数字通常是不可移植的,但kind=32 对我知道的所有编译器都无效。使用kind=real32kind=real64(常量来自模块iso_fortran_envgcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html)。它们应该等同于 NumPy float32 和 float64。

标签: python fortran binaryfiles


【解决方案1】:

感谢这个伟大的社区,从我得到的所有建议和一点点修补中,我想我找到了一个稳定的解决方案来解决这个问题,我想与你分享所有这些答案。我将在这里提供一个最小的示例,我想将 Python 中的可变大小数组写入二进制文件,并使用 Fortran 读取它。我假设行数numRows 和列数numCols 也与完整数组datatArray 一起写入。以下 Python 脚本 writeBin.py 写入文件:

import numpy as np
# Read in the numRows and numCols value 
# Read in the array values
numRowArr = np.array([numRows], dtype=np.float32)
numColArr = np.array([numCols], dtype=np.float32)
fileObj   = open('pybin.bin', 'wb')
numRowArr.tofile(fileObj)
numColArr.tofile(fileObj)
for i in range(numRows):
    lineArr = dataArray[i,:]
    lineArr.tofile(fileObj)
fileObj.close()

接下来,从文件中读取数组的fortran代码可以编写如下:

program readBin

    use iso_fortran_env

    implicit none

    integer:: nR, nC, i

    real(kind=real32):: numRowVal, numColVal
    real(kind=real32), dimension(:), allocatable:: rowData
    real(kind=real32), dimension(:,:), allocatable:: fullData

    open(unit=10,file='pybin.bin',form='unformatted',status='old',access='stream')

    read(unit=10) numRowVal
    nR = int(numRowVal)

    read(unit=10) numColVal
    nC = int(numColVal)

    allocate(rowData(nC))
    allocate(fullData(nR,nC))

    do i = 1, nR

        read(unit=10) rowData
        fullData(i,:) = rowData(:)

    end do

    close(unit=10)

end program readBin

我从这个线程的讨论中收集到的要点是尽可能匹配读取和写入,以及要读取的数据类型的精确规范,它们的写入方式等。您可能会注意到,这是一个虚构的例子,所以这里和那里可能有些事情并不完美。但是,我现在已经使用它来编写有限元程序,并且网格数据是我使用此二进制读/写的地方 - 它工作得很好。

P.S:如果发现有错别字,请告诉我,我会立即修改。

非常感谢。

【讨论】:

    【解决方案2】:

    这是一个简单的例子,说明如何将 numpy 生成的数据以二进制方式传输到 Fortran。

    我在[0,2π)上计算了sin的360个值,

    #!/usr/bin/env python3
    import numpy as np
    
    with open('sin.dat', 'wb') as outfile:
        np.sin(np.arange(0., 2*np.pi, np.pi/180.,
                         dtype=np.float32)).tofile(outfile)
    

    使用tofile 将其导出到二进制文件'sin.dat',其大小为1440 bytes (360 * sizeof(float32)),使用此Fortran95 (gfortran -O3 -Wall -pedantic) 程序读取该文件,该程序输出1. - (val**2 + cos(x)**2) for x in [ 0,2π),

    program numpy_import
        integer,         parameter                    :: REAL_KIND = 4
        integer,         parameter                    :: UNIT = 10
        integer,         parameter                    :: SAMPLE_LENGTH = 360
        real(REAL_KIND), parameter                    :: PI = acos(-1.)
        real(REAL_KIND), parameter                    :: DPHI = PI/180.
    
        real(REAL_KIND), dimension(0:SAMPLE_LENGTH-1) :: arr
        real(REAL_KIND)                               :: r
        integer                                       :: i
    
    
        open(UNIT, file="sin.dat", form='unformatted',&
                    access='direct', recl=4)
    
        do i = 0,ubound(arr, 1)
            read(UNIT, rec=i+1, err=100) arr(i)  
        end do
    
        do i = 0,ubound(arr, 1)
            r = 1. - (arr(i)**2. + cos(real(i*DPHI, REAL_KIND))**2) 
            write(*, '(F6.4, " ")', advance='no')&
                real(int(r*1E6+1)/1E6, REAL_KIND)
        end do
    
    100 close(UNIT)    
        write(*,*)
    end program numpy_import
    

    因此,如果val == sin(x),对于 float32 类型,数字结果必须以良好的近似值消失。

    确实:

    输出:

    360 x 0.0000
    

    【讨论】:

      猜你喜欢
      • 2021-12-08
      • 2012-04-29
      • 1970-01-01
      • 2015-02-19
      • 2013-02-17
      • 2021-12-24
      • 2015-07-17
      • 2011-12-26
      • 1970-01-01
      相关资源
      最近更新 更多