【问题标题】:How to shift the bytes using Big Endian format instead of Little Endian in C++?如何在 C++ 中使用 Big Endian 格式而不是 Little Endian 来移动字节?
【发布时间】:2013-10-15 05:30:50
【问题描述】:

我正在使用 Java 以 Big Endian 字节顺序格式将字节数组值写入文件。现在我需要从 C++ 程序中读取该文件...

我正在写入文件的字节数组由三个字节数组组成,如下所述-

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

我正在将employeeIdlastModifiedDateattributeValue 一起写入一个字节数组,然后我将生成的字节数组写入一个文件,然后我将使用我的 C++ 程序来检索该字节数组数据从文件然后反序列化它以从中提取employeeIdlastModifiedDateattributeValue

下面是我的工作 Java 代码,它将字节数组值写入大端格式的文件:

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++ 程序从同一个文件中检索字节数组并将其反序列化以从中提取 employeeIdlastModifiedDateattributeValue。我不确定 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,这意味着我知道我猜每个字节属于哪里。那么如何在将字节移动到每个值的正确位置时对其进行编码呢?现在我将它编码为 little-endian 我猜这不是我想要的......

我在某处读到我可以在 C++ 中使用ntoh 来反序列化字节数组。不确定htons 是否会比我目前拥有的解决方案更好?..

如果是,那么我不确定如何在我当前的 C++ 代码中使用它?

任何人都可以看看 C++ 代码,看看我可以做些什么来改进它,因为我认为它看起来效率不高吗?有没有更好的方法来反序列化字节数组并在 C++ 端提取相关信息?

【问题讨论】:

    标签: c++ bytebuffer endianness


    【解决方案1】:

    如果 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;
      ....
    }
    

    【讨论】:

    • Java 代码是由我编写的,我会将字节数组以特定格式写入文件。然后 C++ 程序将由其他人编写,但我正在为它开发原型现在让它工作......我在C++方面一点也不擅长,但不知何故我设法编写了那个C++代码......如果你能在我在C++中所拥有的基础上为C++提供一个工作示例,是否有可能?这样我就能明白更多了?感谢您的帮助.. 我发现用 C++ 编写 Big-Endian 代码非常困难.. :(
    • 而且我不能使用任何其他序列化格式。这是我们团队负责人决定采用的方式...
    • 看看这个问题:byteBuffer in c++ 及其后续链接:git-hubgoogle
    猜你喜欢
    • 2019-06-30
    • 2012-10-09
    • 2010-10-16
    • 2020-12-01
    • 2011-10-06
    • 2022-12-12
    • 2018-05-15
    • 2022-06-10
    • 1970-01-01
    相关资源
    最近更新 更多