Qt 二进制数据读写详解

Qt 二进制数据读写详解

一、Qt 二进制数据读写讲解

1、核心概念

  1. QByteArray :这是 Qt 中处理原始字节数据的核心容器类。它可以存储任意字节序列(char* 数据)。
  2. QFile:用于访问文件系统中的文件。它可以以文本模式或二进制模式打开文件。
  3. QDataStream :提供了一种序列化的方式,将基本数据类型(如 int, float, QString 等)和更复杂的结构转换成字节流,以便写入文件或网络套接字,并能从字节流中还原出来。它负责处理字节序(Endianness)等问题。
  4. QBuffer :将 QByteArray 当作一个 QIODevice(类似于文件或套接字)来使用,方便进行流式操作。

2、二进制写入操作

2.1、方法一:直接使用 QFileQByteArray

cpp 复制代码
#include <QFile>
#include <QByteArray>

// 准备要写入的二进制数据
QByteArray data;
data.append(0x01); // 写入一个字节 0x01
data.append(0x02); // 写入一个字节 0x02
// ... 可以填充任意字节

// 打开文件 (使用 QIODevice::WriteOnly 和 QIODevice::Truncate 确保清空原有内容)
QFile file("binary.dat");
if (file.open(QIODevice::WriteOnly)) {
    // 将整个 QByteArray 写入文件
    qint64 bytesWritten = file.write(data);
    if (bytesWritten == -1) {
        // 处理写入错误
    }
    file.close(); // 关闭文件
} else {
    // 处理文件打开失败
}

2.2、方法二:使用 QDataStream 进行序列化(推荐)

cpp 复制代码
#include <QFile>
#include <QDataStream>

QFile file("structured.dat");
if (file.open(QIODevice::WriteOnly)) {
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_6_5); // 设置序列化版本,保证兼容性

    // 写入各种类型的数据
    quint8 byteValue = 0xAB;
    qint32 intValue = 123456;
    float floatValue = 3.14159f;
    QString stringValue = "Hello Binary";

    out << byteValue; // 写入一个字节 (quint8)
    out << intValue;  // 写入一个 32 位整数
    out << floatValue; // 写入一个浮点数
    out << stringValue; // 写入一个字符串 (Qt 会处理其内部编码和长度)

    file.close();
} else {
    // 处理错误
}

QDataStream 会自动处理不同平台上可能存在的字节序问题 (大端序 Big-Endian 或小端序 Little-Endian)。默认情况下,它使用大端序。你也可以使用 out.setByteOrder(QDataStream::LittleEndian) 显式设置。

3、二进制读取操作

3.1、方法一:直接使用 QFileQByteArray

cpp 复制代码
#include <QFile>
#include <QByteArray>

QFile file("binary.dat");
if (file.open(QIODevice::ReadOnly)) {
    // 一次性读取整个文件内容到 QByteArray
    QByteArray allData = file.readAll();
    file.close();

    // 处理 allData
    if (allData.size() >= 2) {
        quint8 firstByte = static_cast<quint8>(allData.at(0)); // 读取第一个字节
        quint8 secondByte = static_cast<quint8>(allData.at(1)); // 读取第二个字节
        // ... 根据协议解析后续字节
    }
} else {
    // 处理错误
}

3.2、方法二:使用 QDataStream 进行反序列化(推荐)

cpp 复制代码
#include <QFile>
#include <QDataStream>

QFile file("structured.dat");
if (file.open(QIODevice::ReadOnly)) {
    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_6_5); // 必须与写入时使用的版本一致!

    // 按写入顺序读取数据
    quint8 byteValue;
    qint32 intValue;
    float floatValue;
    QString stringValue;

    in >> byteValue;   // 读取一个字节
    in >> intValue;    // 读取一个 32 位整数
    in >> floatValue;  // 读取浮点数
    in >> stringValue; // 读取字符串

    file.close();

    // 现在可以使用读取到的数据 byteValue, intValue, floatValue, stringValue
} else {
    // 处理错误
}

使用 QDataStream 读取时,顺序和类型必须与写入时完全一致,否则会导致数据错乱或读取失败。QDataStream 会维护一个状态,你可以通过 in.status() 检查是否有错误发生。

4、使用 QBuffer 进行内存读写

cpp 复制代码
#include <QBuffer>
#include <QDataStream>

QByteArray bufferData; // 最终数据将存储在这里
QBuffer buffer(&bufferData);
buffer.open(QIODevice::WriteOnly); // 打开 Buffer 用于写入

QDataStream out(&buffer);
out << QString("Data in Memory") << 42;
buffer.close(); // 此时 bufferData 包含了序列化后的数据

// 从 bufferData 读取
buffer.setBuffer(&bufferData); // 关联同一个 QByteArray
buffer.open(QIODevice::ReadOnly); // 打开 Buffer 用于读取

QDataStream in(&buffer);
QString str;
int num;
in >> str >> num;

buffer.close();

5、注意事项

  1. 字节序 :如果数据需要在不同架构(x86, ARM 等)或不同系统(Windows, Linux, macOS)间交换,务必使用 QDataStream 并统一设置字节序(通常用默认的大端序或显式设置)。
  2. 版本控制QDataStream 的序列化格式在不同 Qt 版本间可能有微小变化。使用 setVersion 指定一致的版本号(如 Qt_6_5)可以保证长期存储或跨版本程序的数据兼容性。
  3. 错误处理 :始终检查文件是否成功打开、读写操作是否成功(write 返回写入字节数,QDataStream::status())。
  4. 类型匹配 :读取时使用的类型必须与写入时完全匹配(例如,写入 qint32 就必须读取 qint32,不能是 int,除非你确定平台上的 int 就是 32 位)。
  5. 性能 :对于大文件,考虑分块读取写入,避免一次性 readAll() 消耗过多内存。
  6. 安全性:读取外部二进制文件时要注意潜在的安全风险(如缓冲区溢出)。

6、示例:一个简单的十六进制查看器片段

cpp 复制代码
#include <QFile>
#include <QDebug>

void viewHex(const QString &filename) {
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug() << "Failed to open file";
        return;
    }

    QByteArray data = file.read(16); // 每次读取 16 字节
    while (!data.isEmpty()) {
        qDebug().noquote() << data.toHex(' ').toUpper(); // 转换为十六进制字符串,空格分隔,大写
        data = file.read(16);
    }

    file.close();
}

二、示例

1、源码

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFile>
#include <QDataStream>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_write_clicked()
{
    // 二进制数据写入
    qDebug()<<"开始写入!";
    QFile writeFile("data.bin");
    if (!writeFile.open(QIODevice::WriteOnly)) {
        qDebug() << "文件打开失败";
        return ;
    }

    QDataStream outStream(&writeFile);
    outStream.setVersion(QDataStream::Qt_6_0); // 设置数据流版本

    int intValue = 42;
    double doubleValue = 3.14159;
    QString stringValue = "Qt二进制示例";

    // 写入不同类型数据
    outStream << intValue << doubleValue << stringValue;
    writeFile.close();

    qDebug()<<"写入成功!";
}


void MainWindow::on_read_clicked()
{
    // 二进制数据读取
    qDebug()<<"开始读取!";
    QFile readFile("data.bin");
    if (!readFile.open(QIODevice::ReadOnly)) {
        qDebug() << "文件打开失败";
        return;
    }
    QDataStream inStream(&readFile);
    inStream.setVersion(QDataStream::Qt_6_0);

    int readInt;
    double readDouble;
    QString readString;

    // 按写入顺序读取数据
    inStream >> readInt >> readDouble >> readString;
    readFile.close();
    qDebug()<<"读取成功!";
    // 验证读取结果
    qDebug() << "读取整数:" << readInt;
    qDebug() << "读取浮点数:" << readDouble;
    qDebug() << "读取字符串:" << readString;
}

代码说明:

  1. 二进制写入

    • 使用 QFileQIODevice::WriteOnly 模式创建文件
    • QDataStream 提供序列化接口
    • 通过 << 操作符写入多种数据类型
  2. 二进制读取

    • QIODevice::ReadOnly 模式打开文件
    • 使用相同的 QDataStream 版本
    • 通过 >> 操作符按写入顺序读取数据
  3. 注意事项

    • 数据流版本需保持一致(Qt_6_0 可替换为实际版本)
    • 读取顺序必须与写入顺序严格一致
    • 支持所有Qt基本类型和容器类(如 QList, QMap
  4. 数据类型支持

    • 基本类型:int, float, double, bool
    • Qt类型:QString, QByteArray, QDateTime
    • 自定义类型(需实现序列化操作符)

2、运行效果



相关推荐
CoderCodingNo2 小时前
【GESP】C++五级真题(贪心考点) luogu-B3872 [GESP202309 五级] 巧夺大奖
开发语言·c++
天桥下的卖艺者2 小时前
R语言演示对没有吸收状态的马尔科夫链分析
开发语言·r语言
q150803962252 小时前
数据整理无忧:深度评测高效文本合并工具的实用功能
开发语言·前端·javascript
小新1102 小时前
Qt 中安全拼接文件路径
开发语言·qt
源代码•宸2 小时前
goframe框架签到系统项目开发(用户认证、基于 JWT 实现认证、携带access token获取用户信息)
服务器·开发语言·网络·分布式·后端·golang·jwt
郝学胜-神的一滴2 小时前
Linux 多线程编程:深入理解 `pthread_join` 函数
linux·开发语言·jvm·数据结构·c++·程序人生·算法
feifeigo1232 小时前
MATLAB微光图像增强综合实现
开发语言·计算机视觉·matlab
黎雁·泠崖2 小时前
C 语言底层核心:数据在内存中的存储(大小端 + 整数 + 浮点型全解析)
c语言·开发语言
崇山峻岭之间2 小时前
Matlab学习记录14
开发语言·学习·matlab