用不同的编译器读写fortran直接访问无格式文件

用不同的编译器读写fortran直接访问无格式文件,fortran,binaryfiles,gfortran,intel-fortran,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编译的其他程序读取数据文件

我在一个程序中有一个部分,它编写了一个直接访问二进制文件,如下所示:

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

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

默认情况下,Ifort和gfortran对记录长度不使用相同的块大小。在ifort中,
open
语句中的
recl
值是4字节块,因此记录长度不是985600字节,而是3942400字节。这意味着记录以390万字节的间隔写入

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

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

  • 在ifort中,指定4字节块中的recl,例如
    320*385*2
    而不是
    *8
  • 在ifort中,使用compile标志
    -假设byterecl
    recl
    值解释为字节
  • 在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 该程序将5个8字节整数的数组写入记录1、2和3中的文件3次。数组是5*8字节,我已经将该数字硬编码为recl值

使用gfortran 5.2的测试用例1 我用命令行编译了这个测试用例:

gfortran -o write-gfortran write.f90
这将生成输出文件(用
od-ad-td8
解释):

5个8字节元素的数组被连续打包到文件中,记录编号2(
101…105
)从我们预期的偏移量40开始,偏移量40是文件
5*8
中的recl值

带有ifort 16的测试用例1 这是类似的编译:

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

数据都在那里,但文件中充满了0值元素。以
*
开头的行表示偏移之间的每一行为0。记录2从偏移量160开始,而不是从40开始。请注意,160是40*4,其中40是我们指定的
5*8
。默认情况下,ifort使用4字节块,因此recl为40表示物理记录大小为160字节

如果使用gfortran编译的代码要读取该值,则记录2、3和4将包含所有0元素,读取记录5将正确读取ifort作为记录2写入的数组。让gfortran read record 2在文件中的另一种方法是使用
recl=160
(4*5*4),以便物理记录大小与ifort写入的内容匹配

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

带有ifort 16和
的测试用例1-假设为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
这将按预期生成文件。命令行参数
-askebyterecl
告诉ifort将任何
recl
值解释为字节,而不是双字(4字节块)。这将产生与使用gfortran编译的代码相匹配的写入和读取

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

输出 gfortran 5.2:

 Using recl=          40
ifort 16,无选项:

 Using recl=          10
ifort 16,
-假设为byterecl

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

总结 要使基于记录的、未格式化的直接数据在ifort和gfortran之间可移植,最简单的选择是只需将
-aspect byterecl
添加到ifort使用的标志中。您真的应该已经这样做了,因为您正在以字节为单位指定记录长度,所以这将是一个简单的更改,可能不会对您产生任何影响


另一种选择是不必担心该选项,而是使用
inquire
内在函数来查询数组的
iolength

你能用文本格式写出来吗?这样你就能看到了?或者它必须是二进制格式?这是一个非常有用的解释。但我要求澄清以下几点:(i)我不理解“字节单位”,我认为记录长度应该是数组的大小×数据类型的大小。(ii)在您建议的第一次修复中
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
 Using recl=          40
 Using recl=          10
 Using recl=          40