当前位置: 首页 > news >正文

QDataStream入门

QDataStream 是 Qt 提供的一个用于二进制数据序列化的类,它可以将各种数据类型转换为字节流,也可以将字节流转换回原始数据。适合用于二进制数据的持久化和网络传输。与 QFileQTcpSocket 等 I/O 设备配合使用。

QDataStream 属性、方法

属性

属性类型描述
byteOrderQDataStream::ByteOrder字节序(大端或小端)
floatingPointPrecisionQDataStream::FloatingPointPrecision浮点数精度(单精度或双精度)
statusQDataStream::Status数据流状态
versionint数据流版本(用于兼容性控制)

主要方法

构造函数

方法描述
QDataStream()创建空的数据流
QDataStream(QIODevice *)关联到指定的 I/O 设备
QDataStream(QByteArray *, QIODevice::OpenMode)关联到字节数组

基本操作

方法描述
QIODevice *device() const获取关联的设备
void setDevice(QIODevice *)设置关联的设备
void unsetDevice()解除设备关联

流控制

方法描述
void skipRawData(int len)跳过指定字节数的数据
int readRawData(char *, int)读取原始数据
int writeRawData(const char *, int)写入原始数据
bool atEnd() const是否到达流末尾

状态管理

方法描述
QDataStream::Status status() const获取当前状态
void resetStatus()重置状态为 Ok
void setStatus(QDataStream::Status)设置流状态

配置方法

方法描述
void setByteOrder(QDataStream::ByteOrder)设置字节序
QDataStream::ByteOrder byteOrder() const获取当前字节序
void setFloatingPointPrecision(QDataStream::FloatingPointPrecision)设置浮点数精度
QDataStream::FloatingPointPrecision floatingPointPrecision() const获取当前浮点数精度
void setVersion(int)设置流版本
int version() const获取当前流版本

枚举类型

ByteOrder

描述
BigEndian大端字节序(网络字节序)
LittleEndian小端字节序(Intel x86字节序)

FloatingPointPrecision

描述
SinglePrecision32位单精度浮点数
DoublePrecision64位双精度浮点数

Status

描述
Ok操作成功
ReadPastEnd尝试读取超过流末尾
ReadCorruptData读取到损坏数据
WriteFailed写入操作失败

重载运算符

运算符支持的数据类型
operator<<基本类型、QString、QByteArray、Qt容器等
operator>>基本类型、QString、QByteArray、Qt容器等

支持的常用内置类型

  • 基本类型:boolcharshortintlong longfloatdouble 等

  • Qt 类型:QStringQByteArrayQBitArrayQDateQTimeQDateTimeQUrlQUuid 等

  • Qt 容器:QListQVectorQMapQHashQSet 等

基本用法

写入数据到流

cpp

#include <QDataStream>
#include <QFile>// 写入数据到文件
QFile file("data.bin");
if (file.open(QIODevice::WriteOnly)) {QDataStream out(&file);out << QString("Hello, Qt!");  // 写入字符串out << 3.1415926;              // 写入双精度浮点数out << 42;                     // 写入整数file.close();
}

从流中读取数据

cpp

#include <QDataStream>
#include <QFile>// 从文件读取数据
QFile file("data.bin");
if (file.open(QIODevice::ReadOnly)) {QDataStream in(&file);QString str;double d;int i;in >> str >> d >> i;  // 注意读取顺序要与写入顺序一致qDebug() << str << d << i;file.close();
}

版本控制

QDataStream 使用版本号来确保数据兼容性:

cpp

QDataStream out(&file);
out.setVersion(QDataStream::Qt_5_15);  // 设置版本QDataStream in(&file);
in.setVersion(QDataStream::Qt_5_15);   // 读取时使用相同版本

自定义数据类型

你可以为自定义类型重载 << 和 >> 运算符:

cpp

struct Person {QString name;int age;
};QDataStream &operator<<(QDataStream &out, const Person &p) {out << p.name << p.age;return out;
}QDataStream &operator>>(QDataStream &in, Person &p) {in >> p.name >> p.age;return in;
}// 使用
Person p {"Alice", 30};
QDataStream out(&file);
out << p;

与 QByteArray 结合使用

cpp

QByteArray byteArray;
QDataStream stream(&byteArray, QIODevice::WriteOnly);
stream << "Qt data stream example";// 从字节数组读取
QDataStream in(byteArray);
QString str;
in >> str;

QDataStream 结构体序列化与反序列化

在 Qt 中使用 QDataStream 对结构体进行序列化和反序列化,主要通过重载 << 和 >> 运算符来实现。以下是详细的使用方法:

基本结构体序列化

1. 定义结构体并重载运算符

cpp

#include <QDataStream>// 自定义结构体
struct Person {QString name;int age;double height;// 可选:添加构造函数方便使用Person() : age(0), height(0.0) {}Person(const QString &n, int a, double h) : name(n), age(a), height(h) {}
};// 重载输出运算符 <<
QDataStream &operator<<(QDataStream &out, const Person &person) {out << person.name << person.age << person.height;return out;
}// 重载输入运算符 >>
QDataStream &operator>>(QDataStream &in, Person &person) {in >> person.name >> person.age >> person.height;return in;
}

2. 使用示例

cpp

// 写入结构体到文件
void writePersonToFile(const QString &filename, const Person &person) {QFile file(filename);if (file.open(QIODevice::WriteOnly)) {QDataStream out(&file);out.setVersion(QDataStream::Qt_5_15); // 设置版本out << person; // 使用重载的运算符file.close();}
}// 从文件读取结构体
Person readPersonFromFile(const QString &filename) {Person person;QFile file(filename);if (file.open(QIODevice::ReadOnly)) {QDataStream in(&file);in.setVersion(QDataStream::Qt_5_15); // 与写入时版本一致in >> person; // 使用重载的运算符file.close();}return person;
}// 使用示例
Person p1("张三", 30, 175.5);
writePersonToFile("person.dat", p1);Person p2 = readPersonFromFile("person.dat");
qDebug() << "Name:" << p2.name << "Age:" << p2.age << "Height:" << p2.height;

包含容器成员的结构体

如果结构体包含容器成员(如 QList、QVector 等),同样可以序列化:

cpp

struct Team {QString teamName;QList<Person> members;
};QDataStream &operator<<(QDataStream &out, const Team &team) {out << team.teamName << team.members;return out;
}QDataStream &operator>>(QDataStream &in, Team &team) {in >> team.teamName >> team.members;return in;
}

版本兼容性处理

对于可能需要变更的结构体,可以添加版本控制:

cpp

struct Employee {QString name;int id;QString department;// 新版本添加的字段QDate hireDate;
};QDataStream &operator<<(QDataStream &out, const Employee &emp) {out << emp.name << emp.id << emp.department;// 只在较新版本中写入hireDateif (out.version() >= QDataStream::Qt_5_12) {out << emp.hireDate;}return out;
}QDataStream &operator>>(QDataStream &in, Employee &emp) {in >> emp.name >> emp.id >> emp.department;// 如果流版本足够新且有数据,读取hireDateif (in.version() >= QDataStream::Qt_5_12 && !in.atEnd()) {in >> emp.hireDate;} else {emp.hireDate = QDate(); // 设为无效日期}return in;
}

结构体转换为字节流

方法1:直接内存拷贝(推荐用于POD)

cpp

#pragma pack(push, 1)  // 1字节对齐
struct PodExample {int id;double value;char name[32];
};
#pragma pack(pop)      // 恢复默认对齐// 转换为字节流
QByteArray structToByteArray(const PodExample &data) {// 直接内存拷贝return QByteArray(reinterpret_cast<const char*>(&data), sizeof(PodExample));
}// 从字节流恢复
PodExample byteArrayToStruct(const QByteArray &byteArray) {PodExample data;if(byteArray.size() == sizeof(PodExample)) {memcpy(&data, byteArray.constData(), sizeof(PodExample));}return data;
}

方法2:使用QDataStream(更通用但稍慢)

cpp

QByteArray structToByteArraySafe(const PodExample &data) {QByteArray byteArray;QDataStream stream(&byteArray, QIODevice::WriteOnly);//stream.setByteOrder(QDataStream::BigEndian);默认就是大端对齐stream.writeRawData(reinterpret_cast<const char*>(&data), sizeof(PodExample));return byteArray;
}PodExample byteArrayToStructSafe(const QByteArray &byteArray) {PodExample data;if(byteArray.size() == sizeof(PodExample)) {QDataStream stream(byteArray);//stream.setByteOrder(QDataStream::BigEndian);默认就是大端对齐stream.readRawData(reinterpret_cast<char*>(&data), sizeof(PodExample));}return data;
}

方法3:逐个成员序列化(最安全但代码量大)

cpp

QByteArray structToByteArrayExplicit(const PodExample &data) {QByteArray byteArray;QDataStream stream(&byteArray, QIODevice::WriteOnly);stream << data.id << data.value;stream.writeRawData(data.name, 32);  // 固定长度字符数组return byteArray;
}PodExample byteArrayToStructExplicit(const QByteArray &byteArray) {PodExample data;QDataStream stream(byteArray);stream >> data.id >> data.value;stream.readRawData(data.name, 32);return data;
}

使用示例

cpp

void exampleUsage() {PodExample original;original.id = 42;original.value = 3.14159;strncpy(original.name, "Example", 32);// 方法1使用QByteArray bytes1 = structToByteArray(original);PodExample restored1 = byteArrayToStruct(bytes1);// 方法2使用QByteArray bytes2 = structToByteArraySafe(original);PodExample restored2 = byteArrayToStructSafe(bytes2);qDebug() << "Original:" << original.id << original.value << original.name;qDebug() << "Restored1:" << restored1.id << restored1.value << restored1.name;qDebug() << "Restored2:" << restored2.id << restored2.value << restored2.name;
}

重要注意事项

  1. 字节序问题

    cpp

    // 如果需要跨平台,应处理字节序
    original.id = qToLittleEndian(original.id);  // 转换为小端
  2. 数据校验

    cpp

    // 可添加校验和
    qint16 checksum = qChecksum(bytes1.constData(), bytes1.size());
  3. 调试查看

    cpp

    qDebug() << "Hex dump:" << bytes1.toHex();
  4. 结构体大小验证

    cpp

    static_assert(sizeof(PodExample) == sizeof(int) + sizeof(double) + 32, "结构体大小不符合预期");

http://www.lryc.cn/news/612583.html

相关文章:

  • LeetCode每日一题,2025-8-7
  • JSON、JSONObject、JSONArray详细介绍及其应用方式
  • Self-RAG:基于自我反思的检索增强生成框架技术解析
  • 【感知机】感知机(perceptron)学习策略
  • 阿里云polardb-x 2.0迁移至华为云taurusdb
  • 【感知机】感知机(perceptron)模型与几何解释
  • MySQL数据库索引及底层数据结构
  • 2025国赛数学建模C题详细思路模型代码获取,备战国赛算法解析——决策树
  • 软件架构:系统结构的顶层设计与战略约束
  • Maven入门到精通
  • Cervantes:面向渗透测试人员和红队的开源协作平台
  • 进阶向:AI聊天机器人(NLP+DeepSeek API)
  • 《动手学深度学习》读书笔记—9.6编码器-解码器架构
  • 嵌入式学习---在 Linux 下的 C 语言学习 Day9
  • 河南萌新联赛2025第(四)场【补题】
  • 云端软件工程智能代理:任务委托与自动化实践全解
  • 【golang】基于redis zset实现并行流量控制(计数锁)
  • 【AI智能编程】Trae-IDE工具学习
  • javascript常用实例
  • Dart语言语法与技术重点
  • InfluxDB 集群部署与高可用方案(一)
  • 解决Node.js v12在Apple Silicon(M1/M2)上的安装问题
  • css怪异模式(Quirks Mode)和标准模式(Standards Mode)最明显的区别
  • Java零基础笔记13(Java编程核心:异常、泛型)
  • 数据结构 二叉树(1)二叉树简单了解
  • Python数据可视化:从基础到高级实战指南
  • Pytorch-07 如何快速把已经有的视觉模型权重扒拉过来为己所用
  • C语言的数组与字符串练习题1
  • VINS-Fusion+UWB辅助算法高精度实现
  • KNN算法:从原理到实战应用