Maxbester
Maxbester

Reputation: 2533

Java read binary file (unsigned long long)

I want to convert a C code to Java. It reads a binary file:

int main(int argc, char**argv)
{
    FILE *fd;
    unsigned long trameNumber = 0;
    unsigned long long INDEX;

    fd = fopen(argv[1],"rb");
    if (fd == NULL)
    {
        printf("Usage %s [File]\n", argv[0]);
        exit(1);
    }

    fread(&INDEX, sizeof(INDEX),1, fd);
    printf("INDEX %llx\n",INDEX);
    trameNumber++;

    while (fread(&INDEX, sizeof(INDEX),1, fd) != 0)
    {
        printf("INDEX %llx\n",INDEX);
        trameNumber++;
    }

    fclose(fd);
    printf("%lu", trameNumber);

    return 0;
}

The output with this code looks like:

INDEX 0
INDEX 9800000000000000
INDEX 1801000000000000
INDEX 5001000000000000
INDEX b801000000000000

Here is my Java code. I've tried to do that with BigInteger:

public static final int DATA_BYTE_LENGHT = 8;

public void readBinary(final String readFilePath)
{
    // A 8 byte buffer = 64 bits
    ByteBuffer byteBuffer = ByteBuffer.allocate(DATA_BYTE_LENGHT);

    // Those channels will be used to read/write files
    FileChannel channelFileInput = null;

    BigInteger bigIndex = null;

    try {

        // File to read
        final File fileRead = new File(readFilePath);

        // Channel used to read the file.
        channelFileInput = new FileInputStream(fileRead).getChannel();

        byteBuffer.put(new byte[DATA_BYTE_LENGHT]);
        byteBuffer.rewind();

        // While the file has content
        while( channelFileInput.read(byteBuffer) != -1 ) {

            byteBuffer.rewind();

            // Big integer positive
            bigIndex = new BigInteger(1, byteBuffer.array());

            byteBuffer.rewind();

            System.out.println("INDEX "+bigIndex.toString(16));

            // Clear the buffer
            byteBuffer.put(new byte[DATA_BYTE_LENGHT]);
            byteBuffer.rewind();

        }

    } catch(FileNotFoundException e) {
        System.err.println("The file cannot be read: "+e.getMessage());
    } catch(Exception e) {
        System.err.println(e.getMessage());
    } finally {
        // Close file connections
        IOUtils.closeQuietly(channelFileInput);
    }
}

However, read() doesn't seem to read the file correctly. Because the output is:

INDEX 0
INDEX 98
INDEX 118
INDEX 150
INDEX 1b8

Could it be an endianess problem? How to solve it?

Thanks

Upvotes: 1

Views: 1712

Answers (2)

Joni
Joni

Reputation: 111219

The BigInteger constructor assumes a big-endian representation, while the data in the file seems to be stored in little-endian. To fix this problem you can reverse the bytes in the array that you obtain, or use order method in ByteBuffer to set the endianness and use the long data type:

// before loop
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

// in loop
long bigIndex = byteBuffer.getLong();
byteBuffer.rewind();
System.out.println("INDEX "+Long.toHexString(bigIndex));

Java's long data type is signed though. This may or may not be a problem depending on what you want to do with the numbers afterwards.

Update: If you must use BigInteger you can just reverse the array of bytes like I said earlier, or read the numbers using long and then correct the sign:

BigInteger bi = BigInteger.valueOf(bigIndex & ~Long.MIN_VALUE);
if (bigIndex < 0) bi = bi.setBit(63);

Upvotes: 1

Jon Trauntvein
Jon Trauntvein

Reputation: 4554

Java defines all primitive data types as using big endian. If you are working on an x86 (windows or linux or OSX) platform, your computer is likely using little endian. Endianess is likely the cause of your affliction. You can probably solve the problem using mask and shift operations to reverse the byte order. The answer to this is actually given in this answer

Upvotes: 0

Related Questions