Запись 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();
}