Как сохранить/загрузить пользовательскую структуру в двоичный файл с помощью QDataStream в Qt?

Я собираюсь сохранить структуру в двоичном файле и загрузить ее позже. Я обнаружил, что один из способов — использовать QVariant. Вот упрощенный пример приложения Qt Widget, который я создал. Но когда я запускаю его, двоичный файл остается пустым. Не могли бы вы помочь мне с этим. Кроме того, есть ли лучший способ сделать это?

главное окно.ч:

#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

главное окно.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
#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QMetaType>
#include <QString>

struct myStruct
{
    QString myStringVar;
};

Q_DECLARE_METATYPE(myStruct*)

#endif // MYSTRUCT_H
6); save << aVariant; myFile.close(); /* load the QVariant from binary file */ QDataStream load(&myFile); load.setVersion(QDataStream::Qt
#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QMetaType>
#include <QString>

struct myStruct
{
    QString myStringVar;
};

Q_DECLARE_METATYPE(myStruct*)

#endif // MYSTRUCT_H
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

Комментарий: в качестве справки: это link показывает аналогичную проблему, но я не смог правильно понять/реализовать предложения. Например, я попытался определить новый оператор, как это объясняется здесь, но это не сработало.


person Keyvan    schedule 13.09.2018    source источник
comment
Всегда проверяйте операции ввода-вывода на наличие ошибок. Дальнейшая отладка бессмысленна, пока вы не убедитесь, что ошибок ввода-вывода нет.   -  person hyde    schedule 13.09.2018


Ответы (2)


Прежде всего, вы не должны сохранять указатель, так как это только адрес памяти переменной, то, что должно быть сохранено, это значение. С другой стороны, если вы хотите использовать 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;
}
person eyllanesc    schedule 13.09.2018
comment
Спасибо. Ответ работает. Но поскольку операторы перегрузки принимают QDataStream и myStruct, мне все еще нужно использовать QVariant? Я удалил QVariant и все заработало. Могу ли я определить операторы перегрузки между QDataStream и QVariant, чтобы они были общими, а не специфичными для структуры? - person Keyvan; 13.09.2018
comment
@ K1-ZR Очевидно, вы можете удалить часть QVariant. Не существует общего метода сериализации структуры, так как это будет зависеть от того, что вы хотите сохранить, кроме того, вы можете сохранить только копирование вещей, например, вы не можете сохранить указатель. Если мой ответ поможет вам, не забудьте отметить его как правильный, если вы не знаете, как это сделать, просмотрите тур, который это лучший способ поблагодарить. - person eyllanesc; 14.09.2018

Вот пример, который работал у меня после внесения всех исправлений. В этом примере показано, как я сохраняю/загружаю struct или class в файл, используя QDataStream. Структура, то есть myStruct, состоит из трех членов: int, string и struct, то есть mySubStruct.

  • Для каждого struct/class необходимо перегрузить операторы << и >>.
  • В каждом перегруженном операторе сохранение/загрузка структуры выполняется путем разбиения структуры на ее примитивные элементы. Элементы-примитивы — это типы, поддерживаемые QDataStream.

комментарий 1. В следующем примере операторы перегружаются в файл struct. Чтобы узнать, как перегрузить операторы в class, см. эту ссылку.

комментарий 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;
}
person Keyvan    schedule 14.09.2018