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

0

Я пишу значение Byte Array в файл с использованием Java с форматом Big Endian Byte. Теперь мне нужно прочитать этот файл из программы C++...

Этот байт-массив, который я пишу в файл, состоит из трех байт-массивов, как описано below-

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

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

Ниже приведен мой рабочий код Java, который записывает значение байтового массива в файл с форматом 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();
        }
    }
}

Теперь мне нужно получить байт-массив из того же файла, используя приведенную ниже программу 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;
}

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

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

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

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

Теги:
endianness
bytebuffer

1 ответ

1
Лучший ответ

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

Если вы действительно хотите написать свою собственную реализацию, лучший способ - написать класс буфера, который принимает параметр byte-stream в качестве параметра (например, как параметр конструктора) и сделать некоторые методы доступа 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-на самом деле). Если вы меняете байты самостоятельно, ваш код не переносится (выполняется только на машинах Little-Endian). Если вы используете ntoh, то он также будет работать на такой машине.

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

class MyBuffer{
  uint16_t readEmployeeId()const{return m_Buffer.readuint16(EmployeeId_Pos);}
  ....
  static const uint16_t EmployeeId_Pos = 0;
  ....
}
  • 0
    Я пишу Java-код, и я буду записывать байтовый массив в файл в определенном формате. А потом программа на C ++ будет написана кем-то другим, но я работаю над прототипом, чтобы он работал сейчас ... Я не очень хорош в C ++, но каким-то образом мне удалось написать этот код C ++ ... Возможно ли, если вы можете предоставить рабочий пример для C ++ на основе того, что у меня есть в C ++? По тому, что я смогу понять больше? Спасибо за помощь .. Я нахожу очень трудное время для написания кода Big-Endian на C ++ .. :(
  • 0
    И я не могу использовать какой-либо другой формат сериализации. Именно так решил пойти наш руководитель команды ...
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню