简体   繁体   中英

Writing out a binary file from fortran and reading in C

I'm trying to read a binary file which was generated from a fortran program. I'm getting strange characters in my output, could it be that both fortran and C have different endianess?

You should use

     acces="stream"

and not unformatted. It is directly modelled after CI/O so there should not be any problems understanding it. It is part of Fortran 2003 and all modern compilers implement it AFAIK (even the less advanced like Open64 or NEC).

Both Fortran and C use the same endianness on the same machine, unless you use special compiler features. It can be problematic when transfering files between platforms.

If the Fortran program used

     access="sequential", form="unformatted"

you will have problems with record delimiters.

Files written as

     access="direct", form="unformatted"

could be OK.

Fortran puts a header in its binary files, thus you can only read them directly with Fortran. Use form='unformatted' when writing the binary file in Fortran to avoid this. The way to read unformatted binaries is shown here . For a deeper understanding of how Fortran deals with binary I/O, a good starting point is this threat .

As others (@cup) have asked, how are you writing the file?

IO between Fortran and C can play nice together, as @Vladimir has said be careful with the endianess when writing. If you do not specify they both should be writing/reading natively. It can be a compiler option or an option to open in Fortran.

For example

  • Ifort: -convert big_endian
  • Gfortran: -fconvert=big_endian
  • PGI: -byteswapio

And so on. Please refer to your compiler manual, to figure out it's flag.

So lets assume you write the file as follows:

program w
    implicit none

    integer, parameter :: max_i = 10
    integer, parameter :: max_j = 10

    integer :: i, j
    integer :: iunit, stat
    real    :: x(max_i,max_j)

    do j=1,max_j
            do i=1,max_i
                    x(i,j) = (i - 1) + (j -1)*max_i
            end do
    end do

    iunit = 7
    open(unit=iunit, file="data", iostat=stat, &
         form="unformatted")
    if (stat .ne. 0) then
            write(*,*) "unable to create data file"
            stop 1
    end if

    write(iunit) x

    close(iunit)

end program w

If we no look at the file,

-rw-r--r-- 1 tbrown users 408 Nov 15 13:16 data

Notice it is 408 bytes. As @leeladam has said Fortran typically puts a size header at the start and end of the data, in this case it is using 4 byte record markers. So two record markers and 100 data elements as 4 byte floats:

2 * 4 + (10*10*4) = 408

Gives a good sanity check on the data file.

You could then read it in C with:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

int
main(int argc, char **argv)
{

    int ifd = 0;
    int i = 0;
    int j = 0;
    int max_i = 10;
    int max_j = 10;
    int fsize = 0;
    int size = 0;
    int ierr = 0;
    float x[max_i][max_j];


    if ((ifd = open("data", O_RDONLY)) < 0) {
            perror("Unable to open data");
            return(EXIT_FAILURE);
        }

    /* read the data size at the start */
    if ((ierr = read(ifd, &fsize, 1*sizeof(int))) < 0) {
            perror("Size read error");
            close(ifd);
            return(EXIT_FAILURE);
    }

    /* compute the size of our data variable */
    size = max_i * max_j * sizeof(float);

    /* read the data */
    if (fsize != size) {
            printf("file and data size mismatch: %d != %d\n", fsize, size);
            close(ifd);
            return(EXIT_FAILURE);
    }

    if ((ierr = read(ifd, &x, size)) < 0) {
            perror("Data read error");
            close(ifd);
            return(EXIT_FAILURE);
    }

    /* read the data size at the end */
    if ((ierr = read(ifd, &fsize, 1*sizeof(int))) < 0) {
            perror("Size read error");
            close(ifd);
            return(EXIT_FAILURE);
    }

    close(ifd);

    for (i = 0; i <= max_i -1; ++i) {
            for (j = 0; j <= max_j -1; ++j) {
                printf("i: %d j: %d x: %f\n", i, j, x[i][j]);
        }
    }

    return(EXIT_SUCCESS);
}

Also note you can inspect the file with od .

To see the first record marker:

$ od -d -N 4 data
0000000       400       0
0000004

To see the data:

$ od -j 4 -N 400 -f data
0000004     0.000000e+00    1.000000e+00    2.000000e+00    3.000000e+00
0000024     4.000000e+00    5.000000e+00    6.000000e+00    7.000000e+00
0000044     8.000000e+00    9.000000e+00    1.000000e+01    1.100000e+01

Edit

I forgot to mention, I prefer to always write Fortran binary output as big endian and document it so people always know. Then in C use the xdr (or ntohl ) routines.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM