Запись Long из Java в файл и чтение его на C++

Я хочу создать файл, который содержит ровно 8 байтов, представляющих длинное число без знака. Файл создается с помощью Java, а затем читается C++. Вот как Java создает файл:

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());
        }

    }

}

и вот как C++ читает это:

#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;
}

ожидаемый результат 12345, но вместо этого я получаю 4120793659044003840.

Я не уверен, если я сделал это неправильно в Java или C++. Или обоих. Что я должен был сделать?

2 ответов


Java пишет long в "сетевом порядке", который является большим endian. C++ читает в аппаратном порядке, который в вашем случае немного endian.

это не означает, однако, что ваша программа на C++ должна перевернуть байт для преобразования, поскольку в этом случае оно не на оборудовании с прямым порядком байтов.

C предоставляет специальную группу функций для независимого от платформы преобразования данных в сетевой порядок и из него. Вам нужно использовать htonll, которым преобразует восемь байтов в сетевом порядке в ваш аппаратный порядок.


Java пишет long как байты, закодированные в big endian, гарантировано.

C++ считывает байты, и на вашем компьютере (предположительно Intel x86) они интерпретируются в little endian как целое число. (Big endian на Motorola 68k и других.)

одним из решений вашей проблемы является ручная реконструкция целого числа в C++ переносным способом:

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;

Примечание: ваш код Java может быть упрощен:

DataOutputStream out = new DataOutputStream(new FileOutputStream("myFile"));
try {
    out.writeLong(12345);  // 8 bytes in big endian
} finally {
    out.close();
}