导入Fortran无格式二进制文件

时间:2019-07-09 16:55:32

标签: import binary fortran

我有一个使用Compaq Visual Fortran编译器(大端)生成的未格式化二进制文件。

以下是一些文档说明的内容:

二进制文件以包含数据数组的一般格式编写,并以描述符记录为首:

  1. 一个8个字符的关键字,用于标识块中的数据。
  2. 一个4字节有符号整数,定义块中元素的数量。
  3. 4个字符的关键字,用于定义数据类型。 (INTE,REAL,LOGI,DOUB或CHAR) 标头项目作为单个记录读入。数据跟随新记录上的描述符。数值数组最多可分为1000个项目。物理记录大小与块大小相同。

Additional keyword info:

尝试读取此类数据

module modbin
type rectype
    character(len=8)::key
    integer::data_count
    character(len=4)::data_type
    logical::is_int
    integer, allocatable:: idata(:)
    real(kind=8), allocatable::rdata(:)
end type
contains
subroutine rec_read(in_file, out_rec)
    integer, intent(in):: in_file
    type (rectype), intent(inout):: out_rec
    !
    ! You need to play around with this figure.  It may not be
    ! entirely accurate - 1000 seems to work, 1024 does not
    integer, parameter:: bsize = 1000
    integer:: bb, ii, iimax

    ! read the header
    out_rec%data_count = 0
    out_rec%data_type = '    '
    read(in_file, end = 20) out_rec%key, out_rec%data_count, 
out_rec%data_type
    ! what type is it?
    select case (out_rec%data_type)
    case ('INTE')
        out_rec%is_int = .true.
        allocate(out_rec%idata(out_rec%data_count))

    case ('DOUB')
        out_rec%is_int = .false.
        allocate(out_rec%rdata(out_rec%data_count))
    end select

    ! read the data in blocks of bsize
    bb = 1
    do while (bb .lt. out_rec%data_count)
        iimax = bb + bsize - 1
        if (iimax .gt. out_rec%data_count) iimax = out_rec%data_count
        if (out_rec%is_int) then
            read(in_file) (out_rec%idata(ii), ii = bb, iimax)
        else
            read(in_file) (out_rec%rdata(ii), ii = bb, iimax)
        end if
        bb = iimax + 1
    end do
20      continue
end subroutine rec_read

subroutine rec_print(in_recnum, in_rec)
    integer, intent(in):: in_recnum
    type (rectype), intent(in):: in_rec
    print *, in_recnum, in_rec%key, in_rec%data_count, in_rec%data_type
    ! print out data
    open(unit=12, file='reader.data' , status='old')
 write(12,*)key
 !write(*,'(i5')GEOMINDX
 !write(*,'(i5')ID_BEG
 !write(*,'(i5')ID_END
 !write(*,'(i5')ID_CELL
 !write(*,'(i5')TIME_BEG
 !write(*,'(i5')SWAT
 !format('i5')
      !end do
    close(12)

end subroutine rec_print
end module modbin

program main
use modbin
integer, parameter:: infile=11
! fixed size for now - should really be allocatable
integer, parameter:: rrmax = 500
type (rectype):: rec(rrmax)
integer:: rr, rlast

open(unit=infile, file='TEST1603.SLN0001', form='UNFORMATTED', 
status='OLD', convert='BIG_ENDIAN')
rlast = 0
do rr = 1, rrmax
    call rec_read(infile, rec(rr))
    if (rec(rr)%data_type .eq. '    ') exit
    rlast = rr
    call rec_print(rr, rec(rr))
end do
close(infile)
end program main

此代码可以编译并顺利显示

this

并且不会产生任何错误,但这会写入输出文件

this is what is written in the output file

没有显示有用的数值

相关文件可用here

正确的WRITE语句应生成一个像这样的文件here

我的写声明输出的文件类型错误吗? ,如果是的话,最好的方法是什么? 谢谢

1 个答案:

答案 0 :(得分:0)

以上注释试图将您定向到代码中的(至少)两个问题之一。在子程序rec_print中,您有write(12,*)key用来编写write(12,*)in_rec%key的位置(至少我想这就是您想要的。)

我发现的另一个问题是rec_print使用status='old'打开reader.data,然后在写入密钥后将其关闭。 (此处使用old表示该文件已存在。)每次调用rec_print时,将打开该文件,覆盖第一条记录,然后关闭该文件。解决此问题的一种方法是使用status='unknown'. position='append',尽管在主程序中一次打开文件并让子例程写入文件会更有效。

如果进行这些更改,我将进入数据文件:

INTEHEAD GEOMETRY GEOMINDX ID_BEG
ID_END
ID_CELL TIME_BEG SWAT

关于CONVERT =和派生类型的附带注释:您的程序不受此影响,但是在处理如何使用CONVERT =读取派生类型记录方面存在编译器差异。我认为gfortran会根据其类型转换每个组件,但是我知道Intel Fortran不会转换整个派生类型的读取(或写入)。您正在阅读在两个编译器中都可以使用的单个组件,所以很好,但是我认为值得一提。

如果您想知道为什么Intel Fortran会这样做,这是由于STRUCTURE / RECORD的VAX FORTRAN(CONVERT =来源)和可能使用UNION / MAP(在标准Fortran中不可用)所致。使用联合,无法知道应如何转换特定组件,因此它仅传输字节。我曾向英特尔团队建议,如果不存在UNION,可以放宽这点,但是我确定优先级非常低。