package ch.akuhn.com.mathworks.matlab; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; /** <b>Partial implementation!</b> * <P> * In MATLAB Version 5, a MAT-file is made up of a 128-byte header followed by * one or more data elements. Each data element is composed of an 8-byte tag * followed by the data in the element. The tag specifies the number of bytes in * the data element and how these bytes should be interpreted; that is, should * the bytes be read as 16-bit values, 32-bit values, floating point values or * some other data type. * <P> * By using tags, the Version 5 MAT-file format provides quick access to * individual data elements within a MAT-file. You can move through a MAT-file * by finding a tag and then skipping ahead the specified number of bytes until * the next tag. * * @author akuhn * */ public class OpenMatlab5 { public static void main(String[] args) throws IOException { File file = new File("swiss_roll_data.matlab5"); FileChannel channel = new FileInputStream(file).getChannel(); ByteBuffer scan = channel.map(MapMode.READ_ONLY,0,channel.size()); scan.order(ByteOrder.BIG_ENDIAN); /* * MATLAB Version 5 MAT-files begin with a 128-byte header made up of a * 124 byte text field and two, 16-bit flag fields. <P> The first 124 * bytes of the header can contain text data in human-readable form. * This text typically provides information that describes how the * MAT-file was created. For example, MAT-files created by MATLAB * include the following information in their headers: <UL> <LI>MATLAB * version <LI>Platform on which the file was created <LI>Date and time * the file was created </UL> <STRONG>Programming Note</STRONG> When * creating a MAT-file, you must write data in the first four bytes of * this header. MATLAB uses these bytes to determine if a MAT-file uses * a Version 5 format or a Version 4 format. If any of these bytes * contain a zero, MATLAB will incorrectly assume the file is a Version * 4 MAT-file. */ byte[] chars = new byte[124]; scan.get(chars); String text = new String(chars); System.out.println(text); /* Header Flag Fields The last four bytes in the header are divided into two, 16-bit flag fields (int16). Programming Note Programs that create MAT-files always write data in their native machine format. Programs that read MAT-files are responsible for byte-swapping. Field Value Version Identifies the version of the MATLAB software used to create the MAT-file format. When creating a MAT-file, set this field to 0x0100. Endian Indicator Contains the two characters, M and I, written to the MAT-file in this order, as a 16-bit value. If, when read from the MAT-file as a 16-bit value, the characters appear in reversed order (IM rather than MI), it indicates that the program reading the MAT-file must perform byte-swapping to interpret the data in the MAT-file correctly. * */ short version = scan.getShort(); System.out.println(version); short letters = scan.getShort(); char shouldBeM = (char) (letters >> 8); if (shouldBeM == 'I') scan.order(ByteOrder.LITTLE_ENDIAN); /* Data Element Format Each data element begins with an 8-byte tag followed immediately by the data in the element. Figure2 shows this format. (MATLAB also supports a compressed data element format. See page -9 for more information.) * */ /* Tag The 8-byte data element tag is composed of two, 32-bit fields: •Data Type •Number of Bytes Data Type. The Data Type field specifies how the data in the element should be interpreted, that is, its size and format. The MAT-file format supports many data types including signed and unsigned, 8-bit, 16-bit, 32-bit, and 64-bit data types and a special data type that represents MATLAB arrays. Table1 lists all these data types with the values used to specify them. The table also includes symbols that are used to represent these data types in the examples in this document. Table 1: MAT-File Data Types MAT-File Data Type Value Symbol 8 bit, signed 1 miINT8 8 bit, unsigned 2 miUINT8 16-bit, signed 3 miINT16 16-bit, unsigned 4 miUINT16 32-bit, signed 5 miINT32 32-bit, unsigned 6 miUINT32 IEEE 754 single format 7 miSINGLE Reserved 8 -- IEEE 754 double format 9 miDOUBLE Reserved 10 -- Reserved 11 -- 64-bit, signed 12 miINT64 Number of Bytes. The Number of Bytes field is a 32-bit value that specifies the number of bytes of data in the element. This value does not include the eight bytes of the data element’s tag. 64-bit, unsigned 13 miUINT64 MATLAB array 14 miMATRIX (For more information about this data type, see “Version 5 MATLAB Array Data Element Formats” on page -10.) Table 1: MAT-File Data Types MAT-File Data Type Value Symbol * */ int type = scan.getInt(); System.out.println(type); int size = scan.getInt(); System.out.println(size-(8+8)-(8+8)-(8+8)-(8+8*3*20000)); { assert 6 == scan.getInt() : "type = miUINT32"; assert 8 == scan.getInt() : "size"; int tag = scan.getInt(); byte flags = (byte) (tag >> 8); byte class0 = (byte) tag; assert 0 == flags : "flags = none"; assert 6 == class0 : "class = mxDOUBLE_CLASS"; scan.getInt(); // undefined } { assert 5 == scan.getInt() : "type = miINT32"; assert 8 == scan.getInt() : "size"; int[] dim = new int[8/4]; for (int i = 0; i < dim.length; i++) dim[i] = scan.getInt(); } { assert 1 == scan.getInt() : "type = miINT8"; assert 6 == scan.getInt() : "size"; byte[] name0 = new byte[6]; scan.get(name0); String name = new String(name0); System.out.println(name); scan.position(scan.position() + 2); } { assert 9 == scan.getInt() : "type = miDOUBLE"; assert 3*20000*8 == scan.getInt() : "size"; System.out.println(scan.remaining()); double[][] data = new double[3][20000]; for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { data[i][j] = scan.getDouble(); } } } { System.out.println(scan.remaining()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println(scan.getInt()); System.out.println((char) scan.get()); System.out.println((char) scan.get()); System.out.println((char) scan.get()); System.out.println((char) scan.get()); System.out.println((char) scan.get()); System.out.println((char) scan.get()); System.out.println((char) scan.get()); System.out.println((char) scan.get()); } } }