Как сдвинуть байты, используя формат Big Endian вместо Little Endian в C ++?

Я записываю значение байтового массива в файл, используя Java с форматом байтового порядка Big Endian. Теперь мне нужно прочитать этот файл из программы C ++ …

Этот байтовый массив, который я записываю в файл, состоит из трех байтовых массивов, как описано ниже:

short employeeId = 32767;
long lastModifiedDate = "1379811105109L";
byte[] attributeValue = os.toByteArray();

я сейчас пишу employeeId , lastModifiedDate а также attributeValue вместе в один байтовый массив и полученный в результате байтовый массив я записываю в файл, а затем у меня будет программа на С ++, которая будет извлекать данные этого байтового массива из файла, а затем десериализовать их для извлечения employeeId, lastModifiedDate а также attributeValue от него.

Ниже мой рабочий Java-код, который записывает значение Byte Array в файл с форматом Big Endian:

public class ByteBufferTest {

public static void main(String[] args) {

String text = "Byte Array Test For Big Endian";
byte[] attributeValue = text.getBytes();

long lastModifiedDate = 1289811105109L;
short employeeId = 32767;

int size = 2 + 8 + 4 + attributeValue.length; // short is 2 bytes, long 8 and int 4

ByteBuffer bbuf = ByteBuffer.allocate(size);
bbuf.order(ByteOrder.BIG_ENDIAN);

bbuf.putShort(employeeId);
bbuf.putLong(lastModifiedDate);
bbuf.putInt(attributeValue.length);
bbuf.put(attributeValue);

bbuf.rewind();

// best approach is copy the internal buffer
byte[] bytesToStore = new byte[size];
bbuf.get(bytesToStore);

writeFile(bytesToStore);

}

/**
* Write the file in Java
* @param byteArray
*/
public static void writeFile(byte[] byteArray) {

try{
File file = new File("bytebuffertest");

FileOutputStream output = new FileOutputStream(file);
IOUtils.write(byteArray, output);

} catch (Exception ex) {
ex.printStackTrace();
}
}
}

Теперь мне нужно извлечь Byte Array из этого же файла с помощью приведенной ниже программы на C ++ и десериализовать его для извлечения. employeeId, lastModifiedDate а также attributeValue от него. Я не уверен, что является лучшим способом на стороне C ++. Ниже приведен код, который у меня есть:

int main() {

string line;

std::ifstream myfile("bytebuffertest", std::ios::binary);

if (myfile.is_open()) {

uint16_t employeeId;
uint64_t lastModifiedDate;
uint32_t attributeLength;

char buffer[8]; // sized for the biggest read we want to do

// read two bytes (will be in the wrong order)
myfile.read(buffer, 2);

// swap the bytes
std::swap(buffer[0], buffer[1]);

// only now convert bytes to an integer
employeeId = *reinterpret_cast<uint16_t*>(buffer);

cout<< employeeId <<endl;

// read eight bytes (will be in the wrong order)
myfile.read(buffer, 8);

// swap the bytes
std::swap(buffer[0], buffer[7]);
std::swap(buffer[1], buffer[6]);
std::swap(buffer[2], buffer[5]);
std::swap(buffer[3], buffer[4]);

// only now convert bytes to an integer
lastModifiedDate = *reinterpret_cast<uint64_t*>(buffer);

cout<< lastModifiedDate <<endl;

// read 4 bytes (will be in the wrong order)
myfile.read(buffer, 4);

// swap the bytes
std::swap(buffer[0], buffer[3]);
std::swap(buffer[1], buffer[2]);

// only now convert bytes to an integer
attributeLength = *reinterpret_cast<uint32_t*>(buffer);

cout<< attributeLength <<endl;

myfile.read(buffer, attributeLength);// now I am not sure how should I get the actual attribute value here?

//close the stream:
myfile.close();
}

else
cout << "Unable to open file";

return 0;
}

Я специально установил для стороны хранения значение big-endian, что означает, что я знаю, где находится каждый байт. Итак, как мне его кодировать, сдвигая байты в правильную позицию для каждого значения? Прямо сейчас я кодирую это как little-endian, я думаю, что это не то, что я хочу …

Я читал где-то, что я могу использовать ntoh в C ++ для десериализации байтового массива .. Не уверен, что htons будет намного лучшим решением по сравнению с тем, что у меня сейчас? ..

Если да, то я не уверен, как использовать это в моем текущем коде C ++?

Кто-нибудь может взглянуть на код C ++ и посмотреть, что я могу сделать, чтобы улучшить его, так как я не думаю, что он выглядит намного эффективнее? Есть ли лучший способ десериализации байтового массива и извлечения соответствующей информации на стороне C ++?

0

Решение

Если вы разрабатываете код Java и C ++, возможно, лучше использовать буфер протокола Google (https://developers.google.com/protocol-buffers/docs/overview) вместо того, чтобы написать свой собственный сериализатор / десериализатор.

Если вы действительно хотите написать свою собственную реализацию, лучший способ — написать класс буфера, который принимает поток байтов в качестве параметра (например, в качестве параметра конструктора) и сделать несколько методов доступа readShort / readLong / readInt / readByte … и только подкачку необходимые байты.

class ByteBuffer{
explicit ByteBuffer(uint8_t* byteStream, uint16_t streamLength);
uint8_t readUInt8(uint16_t readPos)const {return m_byteStream[readPos];} // no conversion needed
uint16_t readUInt16(uint16_t readPos)const {
const uint8_t byteCount = 2;
union{
uint16_t u16;
uint8_t u8[byteCount];
}tmp;
for(uint8_t i=0; i<byteCount; ++i){
tmp.u8[i] = readUInt8(readPos+i*8);
}
return ntohs(tmp.u16); // do conversion
}
...
}

Чеки на чтение за буфером здесь отсутствуют.
Если ваш код должен быть переносимым, вы должны использовать ntohl / ntohs (см .: http://forums.codeguru.com/showthread.php?298741-C-General-What-do-ntohl%28%29-and-htonl%28%29-actually-do).
Если вы поменяете байты своими собственными, то ваш код не будет переносимым (работает только на машинах Little-Endian). Если вы используете ntoh, то он также будет работать на такой машине.

Для удобства я бы также написал класс-обертку, где вы можете читать и писать свои поля (например, employeeId) напрямую:

class MyBuffer{
uint16_t readEmployeeId()const{return m_Buffer.readuint16(EmployeeId_Pos);}
....
static const uint16_t EmployeeId_Pos = 0;
....
}
1

Другие решения

Других решений пока нет …