QDataStream入门
QDataStream 是 Qt 提供的一个用于二进制数据序列化的类,它可以将各种数据类型转换为字节流,也可以将字节流转换回原始数据。适合用于二进制数据的持久化和网络传输。与 QFile
、QTcpSocket
等 I/O 设备配合使用。
QDataStream 属性、方法
属性
属性 | 类型 | 描述 |
---|---|---|
byteOrder | QDataStream::ByteOrder | 字节序(大端或小端) |
floatingPointPrecision | QDataStream::FloatingPointPrecision | 浮点数精度(单精度或双精度) |
status | QDataStream::Status | 数据流状态 |
version | int | 数据流版本(用于兼容性控制) |
主要方法
构造函数
方法 | 描述 |
---|---|
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
值 | 描述 |
---|---|
SinglePrecision | 32位单精度浮点数 |
DoublePrecision | 64位双精度浮点数 |
Status
值 | 描述 |
---|---|
Ok | 操作成功 |
ReadPastEnd | 尝试读取超过流末尾 |
ReadCorruptData | 读取到损坏数据 |
WriteFailed | 写入操作失败 |
重载运算符
运算符 | 支持的数据类型 |
---|---|
operator<< | 基本类型、QString、QByteArray、Qt容器等 |
operator>> | 基本类型、QString、QByteArray、Qt容器等 |
支持的常用内置类型
基本类型:
bool
,char
,short
,int
,long long
,float
,double
等Qt 类型:
QString
,QByteArray
,QBitArray
,QDate
,QTime
,QDateTime
,QUrl
,QUuid
等Qt 容器:
QList
,QVector
,QMap
,QHash
,QSet
等
基本用法
写入数据到流
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;
}
重要注意事项
字节序问题:
cpp
// 如果需要跨平台,应处理字节序 original.id = qToLittleEndian(original.id); // 转换为小端
数据校验:
cpp
// 可添加校验和 qint16 checksum = qChecksum(bytes1.constData(), bytes1.size());
调试查看:
cpp
qDebug() << "Hex dump:" << bytes1.toHex();
结构体大小验证:
cpp
static_assert(sizeof(PodExample) == sizeof(int) + sizeof(double) + 32, "结构体大小不符合预期");