Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
76 Views

Reading an unformatted file from Java

Jump to solution

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

Accepted Solutions
Highlighted
Black Belt
76 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
Highlighted
Black Belt Retired Employee
76 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
Highlighted
Beginner
76 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
Highlighted
Black Belt Retired Employee
76 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
Highlighted
Black Belt
77 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
Highlighted
Beginner
76 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