Reading FORTRAN unformatted binary files in C/C++

Or....FORTRAN Weirdness, what were they thinking?

Written by Paul Bourke
April 2003


Problem

Ever wanted to read binary files written by a FORTRAN program with a C/C++ program? Not such an unusual or unreasonable request but FORTRAN does some strange things ..... consider the following FORTRAN code, where "a" is a 3D array of 4 byte floating point values.

        open(60,file=filename,status='unknown',form='unformatted')
        write(60) nx,ny,nz
        do k = 1,nz
          do j = 1,ny
           write(60) (a(i,j,k),i=1,nx)
          enddo
        enddo
        close(60)

What you will end up with is not a file that is (4 * nx) * ny * nz + 12 bytes long as it would be for the equivalent in most (if not all) other languages! Instead it will be nz * ny * (4 * nx + 8) + 20 bytes long. Why?

Reason

Each time the FORTRAN write is issued a "record" is written, the record consists of a 4 byte header, then the data, then a trailer that matches the header. The 4 byte header and trailer consist of the number of bytes that will be written in the data section. So the following

        write(60) nx,ny,nz
gets written on the disk as follows where nx,ny,nz are each 4 bytes, the other numbers below are 2 byte integers written in decimal
        0 12 nx ny nz 0 12
The total length written is 20 bytes. Similarly, the line
        write(60) (a(i,j,k),i=1,nx)
gets written as follows assuming nx is 1024 and "a" is real*4
        0 4096 a(1,j,k) a(2,j,k) .... a(1024,j,k) 0 4096

The total length is 4104 bytes. Fortunately, once this is understood, it is a trivial to read the correct things in C/C++.

A consequence that is a bit shocking for many programmers is that the file created with the above code gives a file that is about 1/3 the size than one created with this code.

        open(60,file=filename,status='unknown',form='unformatted')
        write(60) nx,ny,nz
        do k = 1,nz
          do j = 1,ny
            do i = 1,nx
              write(60) a(i,j,k)
            enddo
          enddo
        enddo
        close(60)

In this case each element of a is written in one record and consumes 12 bytes for a total file size of nx * ny * nz * 12 + 20.

Note