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;
    // 只在较新版本中写入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;
}

重要注意事项

  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, 
                 "结构体大小不符合预期");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byxdaz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值