如何使用Qt中的QDataStream将自定义结构体保存/加载到二进制文件中?

6

我将要保存一个结构体到二进制文件中,并且在以后加载它。我发现一种方法是使用 QVariant。这里是我创建的一个简化的 Qt Widget应用程序示例。但是当我运行它时,二进制文件仍然为空。请帮助我解决这个问题。此外,有没有更好的方法来做这样的事情?

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFile>
#include <QFileDialog>
#include <QDataStream>
#include <QString>

#include "mystruct.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    /* create an object of the struct */
    myStruct * aStruct = new myStruct;
    aStruct->myStringVar = QString("aaaaa");

    /* put the object in QVariant */
    QVariant aVariant;
    aVariant.setValue(aStruct);

    /* save the QVariant to binary file */
    QFile myFile("myFile");
    QDataStream save(&myFile);
    save.setVersion(QDataStream::Qt_4_6);
    save << aVariant;
    myFile.close();

    /* load the QVariant from binary file */
    QDataStream load(&myFile);
    load.setVersion(QDataStream::Qt_4_6);
    QVariant theVariant;
    load >> theVariant;
    QString theVar = theVariant.value<myStruct*>()->myStringVar;
    myFile.close();
}

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

mystruct.h:

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QMetaType>
#include <QString>

struct myStruct
{
    QString myStringVar;
};

Q_DECLARE_METATYPE(myStruct*)

#endif // MYSTRUCT_H

注释: 作为参考,这个链接显示了一个类似的问题,但我不能正确地理解/实现建议。例如,我试图定义一个新的运算符,就像这里所解释的那样,但它没有起作用。


始终检查IO操作是否存在错误。在确认没有任何IO错误之前,进一步调试是毫无意义的。 - hyde
2个回答

11

首先,您不应该保存指针,因为这只是变量的内存地址,应该保存的是值。另一方面,如果您想使用QDataStream,您必须重写流运算符,最后,如果您想保存为QVariant,您必须使用qRegisterMetaTypeStreamOperators。除以上之外,您的代码还存在一些错误,比如没有打开QFile。

mystruct.h

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QString>
#include <QMetaType>

struct myStruct
{
    QString myStringVar;
    bool operator==(const myStruct & o){
        return o.myStringVar == this->myStringVar;
    }
    friend QDataStream &operator<<(QDataStream &out, const myStruct &rhs){
        out << rhs.myStringVar;
        return out;
    }
    friend QDataStream &operator>>(QDataStream &in, myStruct &rhs){
        in >> rhs.myStringVar;
        return in;
    }
};
Q_DECLARE_METATYPE(myStruct)

#endif // MYSTRUCT_H

main.cpp

#include "mystruct.h"

#include <QDataStream>
#include <QFile>
#include <QVariant>

int main(int argc, char *argv[])
{
    qRegisterMetaTypeStreamOperators<myStruct>("myStruct");

    // create struct
    myStruct aStructIn{"aaa"};

    // myStruct to QVariant
    QVariant aVariant;
    aVariant.setValue(aStructIn);

    //open file
    QFile myFile("myFile");
    if(!myFile.open(QIODevice::WriteOnly))
        return -1;

    // save QVariant
    QDataStream save(&myFile);
    save.setVersion(QDataStream::Qt_4_6);
    save << aVariant;
    myFile.close();

    //open file
    if(!myFile.open(QIODevice::ReadOnly))
        return -1;

    // read QVariant
    QDataStream load(&myFile);
    load.setVersion(QDataStream::Qt_4_6);
    QVariant theVariant;
    load >> theVariant;
    myFile.close();

    // QVariant to myStruct
    myStruct aStructOut = theVariant.value<myStruct>();

    Q_ASSERT(aStructOut == aStructIn);

    return 0;
}

谢谢。答案可行。但是由于重载运算符采用了“QDataStream”和“myStruct”,所以我是否仍然需要使用“QVariant”?我删除了“QVariant”后它可以工作。我能否定义在“QDataStream”和“QVariant”之间的重载运算符,以便它可以通用,而不是特定于一个结构体? - Keyvan
1
@K1-ZR 显然,你可以删除 QVariant 部分。由于序列化结构取决于要保存的内容,因此没有通用方法,此外,你只能拷贝可保存的事物,例如你无法保存指针。如果我的答案帮助到了你,请不要忘记将其标记为正确的答案,如果你不知道如何操作,请查看 [tour],那是最好的感谢方式。 - eyllanesc

2
这是一个在所有更正后对我有效的示例。该示例展示了如何使用 QDataStream 将一个structclass保存/加载到文件中。结构体,即myStruct,有三个成员:一个int,一个string和一个struct,即mySubStruct
  • 对于每个struct/class,需要重载<<>>运算符。
  • 在每个重载的运算符中,通过将结构体分解为其原始成员来完成结构体的保存/加载。原始成员是 QDataStream支持的类型
评论1:下面的例子使用运算符重载到一个结构体中。如何将运算符重载到类中,请参见此链接
评论2:此链接是一个完整的示例。

mysubstruct.h

#ifndef MYSUBSTRUCT_H
#define MYSUBSTRUCT_H

#include <QString>
#include <QDataStream>

struct mySubStruct
{
    int intVar;
    QString strVar;

    /* overload the operators */
    friend QDataStream &operator<< (QDataStream &out, const mySubStruct &rhs)
    {
        out << rhs.intVar << rhs.strVar;
        return out;
    }

    friend QDataStream &operator>> (QDataStream &in, mySubStruct &rhs)
    {
        in >> rhs.intVar >> rhs.strVar;
        return in;
    }
};
#endif // MYSUBSTRUCT_H

mystruct.h

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QString>
#include <QDataStream>
#include "mysubstruct.h"

struct myStruct
{
    int intVar;
    QString strVar;
    mySubStruct subStructObj;

    /* overload the operators */
    friend QDataStream &operator<< (QDataStream &out, const myStruct &rhs)
    {
        out << rhs.intVar << rhs.strVar << rhs.subStructObj;
        return out;
    }

    friend QDataStream &operator>> (QDataStream &in, myStruct &rhs)
    {
        in >> rhs.intVar >> rhs.strVar >> rhs.subStructObj;
        return in;
    }
};
#endif // MYSTRUCT_H

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFile>
#include <QString>
#include <QDebug>
#include "mystruct.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

"mainwindow.cpp"展示了操作符如何被实现。
#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    /* create struct */
    mySubStruct subStructIn;
    subStructIn.intVar = 1;
    subStructIn.strVar = "a";

    myStruct structIn;
    structIn.intVar = 11;
    structIn.strVar = "aa";
    structIn.subStructObj = subStructIn;

     /* open file */
     QFile myFile("myFile");

     /* save */
     if(myFile.open(QIODevice::WriteOnly))
     {
         QDataStream save(&myFile);
         save.setVersion(QDataStream::Qt_4_6);
         save << structIn;
         myFile.close();
     }

     /* load */
     myStruct structOut;
     if(myFile.open(QIODevice::ReadOnly))
     {
         QDataStream load(&myFile);
         load.setVersion(QDataStream::Qt_4_6);
         load >> structOut;
         myFile.close();
     }

     qDebug() << structOut.intVar;
     qDebug() << structOut.strVar;
     qDebug() << structOut.subStructObj.strVar;
     qDebug() << structOut.subStructObj.intVar;
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接