当前位置: 首页 > 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::LittleEndian);默认就是大端对齐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.dtcms.com/a/319926.html

相关文章:

  • Redis实战(8) -- 分布式锁Redission底层机制
  • 如何解决用阿里云效流水线持续集成部署Nuxt静态应用时流程卡住,进行不下去的问题
  • 基于 MATLAB 的 QPSK 调制、解调、通过高斯信道的误码率计算,并绘制误码率图和眼图、星座图
  • 火山引擎接入豆包AI(纯前端调用api的方式)
  • Java 大视界 -- Java 大数据在智能教育在线课程互动优化与学习体验提升中的应用(386)
  • E频段芯片解决方案
  • npm 与 npx 区别详解。以及mcp中npx加载原理。
  • 2025年生成式大模型部署与推理优化全景解析
  • 14天搞定Excel公式:告别加班,效率翻倍!
  • 【YOLOv8改进 - 上采样】EUCB:(Efficient Up-convolution Block,高效上卷积块)实现特征图尺度匹配和高效上采样
  • 网络编程基石:域名系统与默认端口号详解
  • 文章采集发布Destoon网站技巧
  • C语言函数与预编译:模块化编程的精髓
  • 【AI论文】细胞锻造(CellForge):虚拟细胞模型的智能体化设计
  • 上岸AAAI 2025:自适应框架+前沿算法,顶会热点方向
  • 【VLLM篇】:原理-实现
  • 【论文阅读】基于元模型的体系知识图谱构建
  • spring boot学习计划
  • 什么是AI Agents
  • 机器学习算法篇(四)决策树算法
  • XCZU19EG-2FFVB1517I FPGA Xilinx AMD ZynqUltraScale+ MPSoC
  • 如何验证Go代理是否设置成功?
  • 深入探索C++模板实现的单例模式:通用与线程安全的完美结合
  • SpringBoot的优缺点
  • MyBatis 操作数据库
  • Orange的运维学习日记--33.DHCP详解与服务部署
  • Linux 系统启动、systemd target 与 root 密码重置指南
  • vector模拟实现
  • Seelen UI:高效的设计与原型制作工具
  • 解决winform中的listbox实现拖拽时,遇到combox控件会闪烁的问题