用不同的编译器读写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