Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a Long from Java in a file and reading it in C++

I want to create a file that contains exactly 8 bytes representing an unsigned long number. The file is created with Java and then read by C++. This is how Java creates the file:

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;

public class Writer {

    public static void main(String[] args) throws Exception {

        ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
        buffer.putLong(12345);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        stream.write(buffer.array());
        try (FileOutputStream outputStream = new FileOutputStream("myFile")) {
            outputStream.write(stream.toByteArray());
        }

    }

}

And this is how C++ reads it:

#include <iostream>
#include <vector>
#include <fstream>
#include <stdio.h>

using namespace std;

// I use this to get each byte from a file
static std::vector<char> ReadAllBytes(char const* filename) {
    std::ifstream ifs(filename, ios::binary|ios::ate);
    std::ifstream::pos_type pos = ifs.tellg();

    std::vector<char>  result(pos);

    ifs.seekg(0, ios::beg);
    ifs.read(&result[0], pos);

    return result;
}


int main (int argc, char* argv[]) {
    std::vector<char> bytes = ReadAllBytes("myFile");
    std::vector<char>::iterator it = bytes.begin();

    char longBytes[8];
    std::copy(&(*it), &(*it) + 8, longBytes);
    unsigned long value = *((unsigned long*)longBytes);

    std::cout << "Size: " << value;
}

The expected output is 12345, but instead I get 4120793659044003840.

I am not sure if I did it wrong in Java or C++. Or both. What should I have done?

like image 971
Voldemort Avatar asked Aug 15 '15 19:08

Voldemort


2 Answers

Java writes long in "network order", which is big endian. C++ reads in hardware order, which in your case is little endian.

It does not mean, however, that your C++ program should flip the bytes for conversion, because in this case it would fail on hardware with big endian order.

C provides a special group of functions for platform-independent conversion of data to and from network order. You need to use htonll function, which converts eight bytes in network order to your hardware order.

like image 78
Sergey Kalinichenko Avatar answered Nov 17 '22 00:11

Sergey Kalinichenko


Java writes the long as bytes encoded in big endian, guaranteed.

C++ reads the bytes, and on your machine (assumed to be Intel x86) they are interpreted in little endian as an integer. (Big endian on Motorola 68k and others.)

One solution to your problem is to manually reconstruct the integer in C++ in a portable way:

uint64_t value =
    (uint64_t)(b[0] & 0xFF) << 56 |
    (uint64_t)(b[1] & 0xFF) << 48 |
    (uint64_t)(b[2] & 0xFF) << 40 |
    (uint64_t)(b[3] & 0xFF) << 32 |
    (uint64_t)(b[4] & 0xFF) << 24 |
    (uint64_t)(b[5] & 0xFF) << 16 |
    (uint64_t)(b[6] & 0xFF) <<  8 |
    (uint64_t)(b[7] & 0xFF) <<  0;

Side note: Your Java code can be simplified:

DataOutputStream out = new DataOutputStream(new FileOutputStream("myFile"));
try {
    out.writeLong(12345);  // 8 bytes in big endian
} finally {
    out.close();
}
like image 27
Nayuki Avatar answered Nov 17 '22 00:11

Nayuki