Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Reading an unformatted file from Java

renaud_egal_r_
Beginner
763 Views

Hello folks,

for a compatibility matter I want to read a fortran unformatted file with another language (here java)

But I read a lot of documentation on this subject, and it seems that this file has very specific format that I quite don't get.

	REAL(8) :: test
	test = 23.1
	OPEN(234, FILE="unformatted1.txt", FORM='UNFORMATTED', STATUS='REPLACE') !ATTENTION, on remplace le fichier s'il est déjà ouvert par qqn d'autre !!!
	WRITE(234) test
	CLOSE (234)

When I read this output file with java, I get [4, 0, 0, 0, -51, -52, -72, 65, 4, 0, 0, 0]

In this piece of code, I just want to write a real*8. Question is, what's really written in the output file ? What should I try to read ? bytes ? Shall I find a header/footer ? How do I retrieve my value from this. If I can...

 

Thanks !

0 Kudos
1 Solution
mecej4
Honored Contributor III
763 Views

Fortran unformatted files have a vendor-specific format, and you will have to know details of that format before you can read such an unformatted file in Java. The structure of Intel Fortran unformatted files is described at https://software.intel.com/en-us/fortran-compiler-18.0-developer-guide-and-reference-record-types .  For the example you showed, in which a single 32-bit real number is written to a file, the contents of the file are : (i) 4 byte record length; (ii) 4 byte 32-bit real; (iii) 4 byte record length. Each of these items is written in little-endian byte order. Java uses "network byte order", i.e., big-endian byte order. Here is one way of reading the file in Java (note that I changed the name of the file ending from ".txt" to ".bin", since the file is certainly not a text file).

import java.io.*;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;


public class UnfrmtEx {

    public static void main(String[] args) throws IOException {
        DataInputStream dis =
                new DataInputStream(
                        new FileInputStream("unformatted1.bin"));
        byte[] ibuf = new byte[4];
        int nbytes = dis.read(ibuf); 
        int   rh   = ByteBuffer.wrap(ibuf).order(ByteOrder.LITTLE_ENDIAN).getInt();
        nbytes = dis.read(ibuf); 
        float flt  = ByteBuffer.wrap(ibuf).order(ByteOrder.LITTLE_ENDIAN).getFloat();
        nbytes = dis.read(ibuf); 
        int   rt   = ByteBuffer.wrap(ibuf).order(ByteOrder.LITTLE_ENDIAN).getInt();

        dis.close();

        System.out.println("RECL header     = " + rh);
        System.out.println("flt =             " + flt);
        System.out.println("RECL trailer    = " + rt);
    }
}

If you think that this is rather verbose, I agree, and you may wish to consider other approaches. For instance, you could first create a big-endian version of your data file (in Fortran you can do this conversion easily provided you know the contents of the file). Your Java program can read this big-endian version of the data file, and you would not need any of the ByteBuffer.wrap... lines of code.

View solution in original post

0 Kudos
5 Replies
Steve_Lionel
Honored Contributor III
763 Views

You want to add ACCESS='STREAM' to your OPEN statement. Then you will get just a stream of bytes, without the implementation-specific record structure one gets by default with Fortran.

0 Kudos
renaud_egal_r_
Beginner
763 Views

I can't modify the write statement in the original code. That's the thing making it more difficult !

But let's say I have proper clean byte stream, how do I read a real from this bunch of bytes ? It may seem like a silly question, but I don't see how to do it. Is it like half bytes giving integer part, half bytes giving decimal part (I guess not, but that's an example)

0 Kudos
Steve_Lionel
Honored Contributor III
763 Views

Each WRITE creates a record. An unformatted record consists of a four-byte integer record length, the data, and then a four byte record length again. (This description holds for record sizes up to about 1GB - larger records do it differently.)

For the program you show, the file would be as follows:

08 00 00 00
00 00 00 A0
99 19 37 40
08 00 00 00

A 4-byte length with value 8, the double precision floating value 23.1, then the 4-byte length 8 again.

Looking at your supposed Java output, that doesn't match the program fragment you supplied in that it's obvious the program wrote a single precision REAL(4) value. If I change the type to REAL(4), then I get:

04 00 00 00
CD CC B8 41
04 00 00 00

which is what you show if each byte is interpreted as an integer.

0 Kudos
mecej4
Honored Contributor III
764 Views

Fortran unformatted files have a vendor-specific format, and you will have to know details of that format before you can read such an unformatted file in Java. The structure of Intel Fortran unformatted files is described at https://software.intel.com/en-us/fortran-compiler-18.0-developer-guide-and-reference-record-types .  For the example you showed, in which a single 32-bit real number is written to a file, the contents of the file are : (i) 4 byte record length; (ii) 4 byte 32-bit real; (iii) 4 byte record length. Each of these items is written in little-endian byte order. Java uses "network byte order", i.e., big-endian byte order. Here is one way of reading the file in Java (note that I changed the name of the file ending from ".txt" to ".bin", since the file is certainly not a text file).

import java.io.*;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;


public class UnfrmtEx {

    public static void main(String[] args) throws IOException {
        DataInputStream dis =
                new DataInputStream(
                        new FileInputStream("unformatted1.bin"));
        byte[] ibuf = new byte[4];
        int nbytes = dis.read(ibuf); 
        int   rh   = ByteBuffer.wrap(ibuf).order(ByteOrder.LITTLE_ENDIAN).getInt();
        nbytes = dis.read(ibuf); 
        float flt  = ByteBuffer.wrap(ibuf).order(ByteOrder.LITTLE_ENDIAN).getFloat();
        nbytes = dis.read(ibuf); 
        int   rt   = ByteBuffer.wrap(ibuf).order(ByteOrder.LITTLE_ENDIAN).getInt();

        dis.close();

        System.out.println("RECL header     = " + rh);
        System.out.println("flt =             " + flt);
        System.out.println("RECL trailer    = " + rt);
    }
}

If you think that this is rather verbose, I agree, and you may wish to consider other approaches. For instance, you could first create a big-endian version of your data file (in Fortran you can do this conversion easily provided you know the contents of the file). Your Java program can read this big-endian version of the data file, and you would not need any of the ByteBuffer.wrap... lines of code.

0 Kudos
renaud_egal_r_
Beginner
763 Views

Thank you both, your help is very valuable to me !

mecej4, that is exactly what I was trying to achieve, without any success ! You're rigth, it is quite a verbose solution but that is working like I expected, so I don't mind !

 

0 Kudos
Reply