阅读写作fortran使用不同的编译器直接访问未格式化的文件

时间:2015-09-21 03:04:28

标签: fortran binaryfiles gfortran intel-fortran

我在程序中有一段写入直接访问二进制文件,如下所示:

open (53, file=filename, form='unformatted', status='unknown',
& access='direct',action='write',recl=320*385*8)
write (53,rec=1) ulat
write (53,rec=2) ulng
close(53)

这个程序是用ifort编译的。但是,如果我从使用gfortran编译的其他程序中读取数据文件,则无法正确重建数据。如果读取数据的程序也在ifort中编译,那么我可以正确地重建数据。这是读取数据文件的代码:

OPEN(53, FILE=fname, form="unformatted", status="unknown", access="direct", action="read", recl=320*385*8)
READ(53,REC=2) DAT

我不明白为什么会这样?我可以使用两个编译器正确读取第一条记录,如果我混合使用编译器,它是我无法正确重建的第二条记录。

1 个答案:

答案 0 :(得分:20)

默认情况下,Ifort和gfortran不会使用相同的块大小来记录长度。在ifort中,recl语句中open的值为4字节块,因此您的记录长度不是985,600字节,长度为3,942,400字节。这意味着记录以390万字节的间隔写入。

gfortran使用recl块大小为1个字节,记录长度为985,600个字节。当您读取第一条记录时,一切正常,但是当您读取第二条记录时,您会查看文件中的985,600字节,但数据是3,942,400字节到文件中。这也意味着您在文件中浪费了大量数据,因为您只使用了其大小的1/4。

有几种方法可以解决这个问题:

  • 在ifort中指定4字节块中的recl,例如320*385*2代替*8
  • 在ifort中,使用编译标志-assume bytereclrecl值解释为字节。
  • 在gfortran中补偿大小并使用recl=320*385*32以便您的读取正确定位。

然而,更好的方法是以recl单位大小设计不可知论。您可以使用inquire找出数组的recl。例如:

real(kind=wp), allocatable, dimension(:,:) :: recltest
integer :: reclen
allocate(recltest(320,385))
inquire(iolength=reclen) recltest
deallocate(recltest)
...
open (53, file=filename, form='unformatted', status='unknown',
& access='direct',action='write',recl=reclen)
...
OPEN(53, FILE=fname, form="unformatted", status="unknown", &
access="direct", action="read", recl=reclen)

这会将reclen设置为基于编译器基本单位记录长度的320x385数组所需的值。如果您在编写和读取代码时都可以使用此代码,而无需在ifort中使用编译时标志或者使用编译器之间的硬编码recl差异来补偿编译器。

说明性示例

测试用例1

program test
  use iso_fortran_env
  implicit none

  integer(kind=int64), dimension(5) :: array
  integer :: io_output, reclen, i
  reclen = 5*8 ! 5 elements of 8 byte integers.

  open(newunit=io_output, file='output', form='unformatted', status='new', &
       access='direct', action='write', recl=reclen)

  array = [(i,i=1,5)]  
  write (io_output, rec=1) array
  array = [(i,i=101,105)]
  write (io_output, rec=2) array
  array = [(i,i=1001,1005)]
  write (io_output, rec=3) array

  close(io_output)
end program test

该程序将5个8字节整数的数组写入记录1,2和3中的文件3次。该数组为5 * 8字节,我将该数字硬编码为recl值。

使用gfortran 5.2

的测试用例1

我使用命令行编译了这个测试用例:

gfortran -o write-gfortran write.f90

这将生成输出文件(用od -A d -t d8解释):

0000000                    1                    2
0000016                    3                    4
0000032                    5                  101
0000048                  102                  103
0000064                  104                  105
0000080                 1001                 1002
0000096                 1003                 1004
0000112                 1005
0000120

5个8-bye元素的数组被连续打包到文件中,记录号2(101 ... 105)从我们期望的偏移量40开始,这是文件{{1}中的recl值}}

使用ifort 16的测试用例1

这是类似的编译:

5*8

对于完全相同的代码,这会产生输出文件(用ifort -o write-ifort write.f90 解释):

od -A d -t d8

数据全部存在,但文件中充满了0值元素。以0000000 1 2 0000016 3 4 0000032 5 0 0000048 0 0 * 0000160 101 102 0000176 103 104 0000192 105 0 0000208 0 0 * 0000320 1001 1002 0000336 1003 1004 0000352 1005 0 0000368 0 0 * 0000480 开头的行表示偏移之间的每一行都是0.记录号2从偏移160开始而不是40.注意160是40 * 4,其中40是我们指定的*的recl。默认情况下,ifort使用4字节块,因此recl为40表示物理记录大小为160字节。

如果使用gfortran编译的代码读取此内容,则记录2,3和4将包含所有0个元素,而记录5的读取将正确读取由ifort写入记录2的数组。将gfortran读取记录2放在文件中的替代方法是使用5*8(4 * 5 * 4),以便物理记录大小与ifort写入的大小匹配。

这样做的另一个后果是浪费空间。过度指定recl意味着您使用4倍的必要磁盘空间来存储记录。

使用ifort 16和recl=160

的测试用例1

编译为:

-assume byterecl

并生成输出文件:

ifort -assume byterecl -o write-ifort write.f90

这会按预期生成文件。命令行参数0000000 1 2 0000016 3 4 0000032 5 101 0000048 102 103 0000064 104 105 0000080 1001 1002 0000096 1003 1004 0000112 1005 0000120 告诉ifort将任何-assume byterecl值解释为字节而不是双字(4字节块)。这将产生与gfortran编译的代码匹配的写入和读取。

测试用例2

recl

这个测试用例的唯一区别是我正在查询正确的recl来表示我的40字节数组(5个8字节整数)。

输出

gfortran 5.2:

program test
  use iso_fortran_env
  implicit none

  integer(kind=int64), dimension(5) :: array
  integer :: io_output, reclen, i
  inquire(iolength=reclen) array
  print *,'Using recl=',reclen

  open(newunit=io_output, file='output', form='unformatted', status='new', &
       access='direct', action='write', recl=reclen)

  array = [(i,i=1,5)]  
  write (io_output, rec=1) array
  array = [(i,i=101,105)]
  write (io_output, rec=2) array
  array = [(i,i=1001,1005)]
  write (io_output, rec=3) array

  close(io_output)
end program test

ifort 16,没有选项:

 Using recl=          40

ifort 16, Using recl= 10

-assume byterecl

我们看到gfortran和ifort使用的1字节块, Using recl= 40 假设recl是byterecl,它等于我们的40字节数组。我们还看到,默认情况下,ifort使用recl为10,这意味着10个4字节块或10个双字,两者都意味着40个字节。所有这三个测试用例都可以生成相同的文件输出,并且任何一个编译器的读/写都可以正常工作。

摘要

要在ifort和gfortran之间移植基于记录的,未格式化的直接数据,最简单的选择就是将40添加到ifort使用的标志中。你真的应该这样做,因为你是以字节为单位指定记录长度,所以这将是一个直接的改变,可能对你没有任何影响。

另一种方法是不用担心该选项,并使用-assume byterecl内在函数来查询数组的inquire