1

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

Maxbester
  • 2,435
  • 7
  • 42
  • 70

2 Answers2

1

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);
Joni
  • 108,737
  • 14
  • 143
  • 193
  • Yes I tried this solution with `order()` but I had the same result. And as you can see in my question (C++ code), my numbers are unsigned unfortunately. – Maxbester Nov 21 '13 at 15:25
  • Signed and unsigned numbers only differ in how the most significant bit is treated in operations such as comparison and division, for most purposes whether you use signed or unsigned makes no difference. The `order()` method does not affect the behavior of `array()`, you need to use `getLong()` and similar methods to see the difference it makes. – Joni Nov 21 '13 at 15:48
  • Okay I didn't know I had to convert it to a long in order to use `order()`. Thanks – Maxbester Nov 21 '13 at 15:59
  • The constructor `BigInteger(long)` doesn't exist. [See here](http://docs.oracle.com/javase/6/docs/api/java/math/BigInteger.html). But I understand the idea. I could use `new BigInteger(Long.toHexString(bigIndex), 16)` – Maxbester Nov 21 '13 at 17:17
  • That's true, to convert `long` to `BigInteger` you have to use the [`valueOf` method](http://docs.oracle.com/javase/6/docs/api/java/math/BigInteger.html#valueOf%28long%29), I should have checked before the edit. Using an intermediate hex string like you do works equally well and is probably easier to understand to most developers. – Joni Nov 21 '13 at 17:26
0

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

Community
  • 1
  • 1
Jon Trauntvein
  • 4,453
  • 6
  • 39
  • 69
  • Thanks for the link. For the record, Apache Commons IO offers a very good tool to get rid of endianness issues: [`EndianUtils`](http://commons.apache.org/proper/commons-io/javadocs/api-2.4/). – Maxbester Nov 22 '13 at 15:11