QT 使用QDataStream写入QByteArray数据时遇到的坑

在使用Qt进行序列化和反序列化时,遇到了额外数据插入的问题。文章详细介绍了问题的起因,通过对比QDataStream操作符<<和writeRawData的使用,找到了正确的解决方案。

在做Qt的序列化和反序列化,粘包问题解决的时候,需要自己定义消息的格式。粘包问题解决后,但是发现消息中多了点东西。有问题,先打印出来看看,用QByteArray中的toHex打印16进制是比较好的做法。
起因:

一、起因

每条消息 = 总长度 + 消息号 + 内容

    QByteArray msgContent = "hello";
    quint16 msgId = 1000;
    quint16 msgLen = static_cast<quint16>(msgContent.size());

    QByteArray totalData;
    QDataStream dataStream(&totalData, QIODevice::WriteOnly);

    quint16 totalLen = static_cast<quint16>(msgLen) + sizeof(msgId) + sizeof(quint16);

    dataStream << totalLen << msgId << msgContent;

    qDebug() << "msgContent: " << totalData.toHex(':');

打印出来的东西多了一些:

msgContent:  "00:09:03:e8:00:00:00:05:68:65:6c:6c:6f"

00 00 00 05是多出来的,这个是我不需要,在解决粘包的时候,我会根据消息的总长度分分割。
于是,我写了一些小的测试代码。

二、测试代码

    QByteArray totalData;
    QDataStream dataStream(&totalData, QIODevice::WriteOnly);

    QByteArray msgContent = "hello";
    dataStream << msgContent;

    qDebug() << "msgContent: " << totalData.toHex(':');

先看这个代码里,打印出的内容是什么?

msgContent:  "00:00:00:05:68:65:6c:6c:6f"

00 00 00 05是msgContent的长度,后面是msgContent的内容。

三、解决方法

    QByteArray totalData;
    QDataStream dataStream(&totalData, QIODevice::WriteOnly);

    QByteArray msgContent = "hello";
//    dataStream << msgContent;

    dataStream.writeRawData(msgContent, msgContent.size());

    qDebug() << "msgContent: " << totalData.toHex(':');

现在看,打印的内容是什么?

msgContent:  "68:65:6c:6c:6f"

这才是我需要的。
也可以写成:

    QByteArray totalData;
    QDataStream dataStream(&totalData, QIODevice::WriteOnly);

    QByteArray msgContent = "hello";
//    dataStream << msgContent;

    totalData += msgContent;
//    dataStream.writeRawData(msgContent, msgContent.size());

    qDebug() << "msgContent: " << totalData.toHex(':');

这时候打印的内容是:

msgContent:  "68:65:6c:6c:6f"

参考:
QT 使用QDataStream时遇到的坑
QDataStream传递自定义的数据类型

<think>我们正在讨论的是QDataStream写入数据自动添加的文件头(幻数和版本号)问题。用户希望知道如何禁用或移除这个文件头。 根据之前的讨论,我们知道QDataStream默认会写入一个8字节的文件头(4字节幻数+4字节版本号)。要禁用这个文件头,我们可以使用以下方法: 方法1:使用Qt_1_0版本 在Qt中,QDataStream::Qt_1_0是一个特殊的版本,它不会写入文件头。我们可以通过设置QDataStream的版本为Qt_1_0来实现。 示例代码: ```cpp QFile file("test.dat"); if (file.open(QIODevice::WriteOnly)) { QDataStream out(&file); // 设置版本为Qt_1_0,这样就不会写入文件头 out.setVersion(QDataStream::Qt_1_0); QByteArray data = "Hello"; out << data; file.close(); } ``` 方法2:直接使用QFile写入 如果我们不需要QDataStream提供的序列化功能(比如只写入原始字节),可以直接使用QFile的write方法,这样就不会有文件头。 示例代码: ```cpp QFile file("test.dat"); if (file.open(QIODevice::WriteOnly)) { QByteArray data = "Hello"; file.write(data); file.close(); } ``` 注意:如果使用Qt_1_0版本,那么在读取也必须设置为相同的版本,否则读取会出错(因为读取也会期望有文件头,但写入没有)。 读取示例(对应方法1的写入): ```cpp QFile file("test.dat"); if (file.open(QIODevice::ReadOnly)) { QDataStream in(&file); in.setVersion(QDataStream::Qt_1_0); // 必须与写入版本一致 QByteArray data; in >> data; file.close(); } ``` 重要提示:使用Qt_1_0版本会禁用文件头,但同也意味着我们放弃了版本控制。这意味着如果以后我们改变数据结构,并且使用不同版本的Qt,可能会遇到兼容性问题。因此,如果只是写入简单的原始数据(如QByteArray)并且不需要跨版本兼容,可以使用这种方法。 另外,如果我们已经写入了文件头,但读取想跳过,可以手动读取并忽略前8个字节(但这样不推荐,因为版本号可能变化,而且幻数也可能变化)。 总结:禁用文件头的方法是设置QDataStream的版本为Qt_1_0,并在读写保持一致。 参考引用[2]中提到的QDataStream使用,以及引用[3]中关于QFile和QDataStream的介绍,我们可以知道QDataStream默认会添加头部信息,但通过设置版本可以改变这一行为[^2][^3]。</think>### 如何禁用QDataStream的文件头写入Qt使用QDataStream写入数据,默认会在文件开头添加8字节的头部信息(4字节幻数 + 4字节版本标识)。以下是禁用文件头的两种有效方法: #### 方法1:设置兼容版本号(推荐) ```cpp QFile file("data.dat"); file.open(QIODevice::WriteOnly); QDataStream out(&file); // 关键设置:使用Qt_1_0版本禁用文件头 out.setVersion(QDataStream::Qt_1_0); // 禁用头部写入[^3] QByteArray data = "Hello Qt"; out << data; // 现在只写入原始数据 file.close(); ``` **原理**: `Qt_1_0`是Qt 1.x代的序列化版本,该版本设计尚未引入文件头机制。设置此版本后: 1. 不写入幻数(Magic Number) 2. 不写入版本标识(Version Tag) 3. 直接输出原始数据内容 #### 方法2:直接使用QFile写入(无序列化) ```cpp QFile file("raw.dat"); file.open(QIODevice::WriteOnly); QByteArray data = "Direct write"; file.write(data); // 完全绕过QDataStream[^1][^3] file.close(); ``` **适用场景**: 当不需要QDataStream的序列化功能: - 直接写入原始字节数据 - 无需处理数据类型转换 - 最高效的二进制写入方式 --- ### 验证效果对比 | 写入方式 | 文件内容(HEX) | 头部大小 | |---------|--------------|---------| | 默认QDataStream | `00 00 01 00 00 00 0D 00 48 65 6C 6C 6F` | 8字节 | | 禁用头部后 | `48 65 6C 6C 6F` | 0字节 | | 直接QFile写入 | `44 69 72 65 63 74` | 0字节 | --- ### 读取的注意事项 **必须保持版本一致**: ```cpp // 读取禁用头部的文件 QFile file("data.dat"); file.open(QIODevice::ReadOnly); QDataStream in(&file); in.setVersion(QDataStream::Qt_1_0); // 必须与写入版本一致[^3] QByteArray data; in >> data; // 正确解析无头部数据 ``` --- ### 各版本特性对比 | QDataStream版本 | 头部写入 | 字节序处理 | 兼容性 | |----------------|---------|-----------|-------| | Qt_1_0 (推荐) | ❌ 禁用 | 无 | 仅基础类型 | | Qt_2_1 ~ 最新 | ✔️ 启用 | 自动处理 | 全功能支持 | | 直接QFile | ❌ 无 | 需手动处理 | 完全可控 | > **关键建议**: > 1. 需要跨平台/版本兼容 ➜ 保留默认头部 > 2. 仅需原始数据存储使用`Qt_1_0`或直接QFile写入 > 3. 处理自定义类型 ➜ 必须保留头部以确保正确序列化[^2][^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值