使用 access=stream 编写大型 Fortran 二进制文件

Writing large Fortran binary files with access=stream

我在理解使用 Fortran 编写的二进制文件的格式时遇到了一些问题。我使用以下子例程将二进制文件写入磁盘:

SUBROUTINE write_field(d,m,outfile)

    IMPLICIT NONE    
    REAL, INTENT(IN) :: d(m,m,m)
    INTEGER, INTENT(IN) :: m
    CHARACTER(len=256), INTENT(IN) :: outfile

    OPEN(7,file=outfile,form='unformatted',access='stream')
    WRITE(7) d
    CLOSE(7)

END SUBROUTINE write_field

我对 access=stream 选项的理解是,这会抑制 Fortran 二进制文件附带的标准页眉和页脚(参见 Fortran unformatted file format)。

如果我用 m=512 编写一个文件,那么我的期望是该文件应该是 4 x 512^3 bytes = 536870912 bytes ~ 513 Mb,但实际上它们比这长 8 个字节,在 536870920 bytes 处进入。我的猜测是这些额外的字节是 4 字节的页眉和页脚,我想通过使用 access='stream'.

来抑制它们

如果我用 m=1024 写一个文件,情况就会让我感到困惑,然后我的期望是该文件应该是 4 x 1024^3 bytes = 4294967296 ~ 4.1 Gb 但实际上它们比这长 24(!) 字节,在 4294967320 bytes 进来。我不明白为什么这里有 24 个额外的字节,这似乎对应于 6(!) 个页眉或页脚。

我的问题是:

(a) 是否可以让 Fortran 编写没有页眉或页脚的二进制文件?

(b) 如果 (a) 的答案是 'no' 那么我能否确保较大的二进制文件与较小的二进制文件具有相同的页眉和页脚结构?

(c) 如果 (a) 和 (b) 的答案都是 'no' 那么我如何理解这些额外的页眉和页脚在文件中的位置。

我正在使用 ifort(版本 14.0.2),我正在一个小型 Linux 集群上编写二进制文件。

更新:当 运行 与 OSx 相同的代码并使用 gfortran 7.3.0 编译时,二进制文件会以预期的大小出现,因为它们总是 4 x m^3 bytes,即使 m=1024。所以这个问题似乎与较旧的编译器有关。

更新:事实上,问题仅在使用 ifort 14.0.2 时出现,我已更新文本以反映这一点。

这个问题通过在 Fortran open 命令中添加 status='replace' 来解决。 与编译器无关

access='stream' 而没有 status='replace',旧的二进制文件不会自动被新的二进制文件替换,只是被覆盖到某个点 (https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/676047)。这导致旧二进制文件简单地将字节替换为新二进制文件的大小,同时保留任何额外字节和文件大小不变。如果新文件大小小于旧文件大小,则会出现问题。问题难以诊断,因为文件 上的时间戳已更新 ,因此使用 ls -l.

查询时文件看起来像是新文件

重现此问题的最小工作示例如下:

PROGRAM write_binary_test_minimal

    IMPLICIT NONE
    REAL :: a

    a=1.

    OPEN(7,file='test',form='unformatted')
    WRITE(7) a
    CLOSE(7)

    OPEN(7,file='test',form='unformatted',access='stream')
    WRITE(7) a
    CLOSE(7)

END PROGRAM write_binary_test_minimal

第一个 write 生成一个大小为 8 + 4 = 12 字节的文件 'test'。 8 是标准的 Fortran 二进制页眉和页脚,4a 的字节大小。在第二个 write 语句中,即使设置了 access='stream',也只会覆盖先前生成的 'test' 的前 4 个字节,使文件大小为 12 字节!解决方法是将第二条write语句改为

OPEN(7,file='test',form='unformatted',access='stream',status='replace')

使用明确的 status='replace' 以确保替换旧文件。