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;
// 只在较新版本中写入hireDate
if (out.version() >= QDataStream::Qt_5_12) {
out << emp.hireDate;
}
return out;
}
QDataStream &operator>>(QDataStream &in, Employee &emp) {
in >> emp.name >> emp.id >> emp.department;
// 如果流版本足够新且有数据,读取hireDate
if (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, "结构体大小不符合预期");