0
votes

I have an unformatted binary file generated using the Compaq Visual Fortran compiler (big endian).

Here's what the little bit of documentation states about it:

The binary file is written in a general format consisting of data arrays, headed by a descriptor record:

  1. An 8-character keyword which identifies the data in the block.
  2. A 4-byte signed integer defining the number of elements in the block.
  3. A 4-character keyword defining the type of data. (INTE, REAL, LOGI, DOUB, or CHAR) The header items are read in as a single record. The data follows the descriptor on a new record. Numerical arrays are divided into block of up to 1000 items. The physical record size is the same as the block size.

Additional keyword info:

Attempts to read such data

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 code compiles and runs smoothly showing

this

and produces no errors but this is written in the output file

this is what is written in the output file

shows me no useful numerical values

The file in question is available here

And the right WRITE statement should produce a file like this one here

Is my WRITE STATEMENT to output this file type wrong? , and if so, what is the best way? thank you

1
A hint: Try using Implicit None - all programs should use this, out it in all your program units.Ian Bush
thanks for the suggestion but it didn't work using implicit Nonejoeykandre
Well as written in the code block above your code shouldn't compile with Implicit None - and if you work out why that is you should be able to work out why the code doesn't work. And in turn that should teach you why you should ALWAYS use Implicit None.Ian Bush

1 Answers

0
votes

The comments above are trying to direct you to one of (at least) two problems in your code. In the subroutine rec_print you have write(12,*)key where you meant to write write(12,*)in_rec%key (at least I think that's what you wanted.)

The other problem I spotted is that rec_print opens reader.data with status='old' and then closes it after writing key. (The use of old here suggests that the file already exists.) Each time rec_print is called, the file is opened, the first record is overwritten, and the file is closed. One solution to this would be to use status='unknown'. position='append', though it would be more efficient to open the file once in the main program and just let the subroutine write to it.

If I make these changes, I get in the data file:

INTEHEAD GEOMETRY GEOMINDX ID_BEG
ID_END
ID_CELL TIME_BEG SWAT

A side-comment about CONVERT= and derived types: Your program isn't affected by this, but there are compiler differences with how reading a derived type record with CONVERT= is handled. I think gfortran converts each component according to its type, but I know that Intel Fortran doesn't convert reads (nor writes) of an entire derived type. You are reading individual components, which works in both compilers, so that's fine, but I thought it was worth mentioning.

If you're wondering why Intel Fortran does it this way, it's due to the VAX FORTRAN (where CONVERT= came from) heritage with STRUCTURE/RECORD and the possible use of UNION/MAP (not available in standard Fortran). With unions, there's no way to know how a particular component should be converted, so it just transfers the bytes. I had suggested to the Intel team that this could be relaxed if no UNIONs were present, but that I'm sure is very low priority.