Qt扫盲-QDataStream 序列化和反序列化理论

2023-10-18 12:15

本文主要是介绍Qt扫盲-QDataStream 序列化和反序列化理论,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

QDataStream 序列化和反序列化理论

  • 一、概述
  • 二、QDataStream 概述
  • 三、版本控制
  • 四、读取和写入原始二进制数据
  • 五、读写Qt集合类
  • 六、读写其他Qt类
  • 七、使用读事务
  • 八、Qt支持的序列化类型

一、概述

序列化: 指的是将一个内存对象转化成一串字节数据(存储在一个字节数组中),可用于保存到本地文件或网络传输。

反序列化: 就是将字节数据还原成内存对象。

序列化是将对象转换为字节流的过程,可以将对象持久化保存在磁盘或者通过网络传输。
反序列化是将字节流转换为对象的过程,可以将保存或传输的对象重新恢复到内存中。

序列化和反序列化的作用包括:

  • 数据持久化:将对象保存到磁盘中,实现数据的长期存储。例如,将用户的配置信息序列化为文件,以便在下次打开应用程序时可以重新加载。
  • 数据传输:通过网络传输对象,实现不同系统之间的数据交互。
  • 分布式计算:在分布式系统中,对象可以在不同节点之间传输和复制,实现数据共享和计算协作。
  • 缓存和缓存同步:将对象序列化后保存在缓存中,提高系统性能,并可以在不同节点之间进行同步和共享缓存数据。
  • 消息传递:在消息队列等异步通信中,通过序列化和反序列化实现消息的发送和接收。
  • 应用状态保存和恢复:将应用程序的状态保存到文件中,以便在下次启动时恢复到之前的状态。

总之,序列化和反序列化在很多场景中都是非常有用的,可以将对象转化为可以存储、传输或共享的格式,以便在需要时可以重新加载和使用。

在Qt官方里提供了一个这样的工具,就是 QDataStream ,注意这里不是Json格式的序列化

Json文本转Qt对象的可以参考

https://github.com/smurfomen/QSerializer

在这里插入图片描述

二、QDataStream 概述

数据流是编码信息的二进制流,它100%独立于主机的操作系统、CPU或字节顺序。例如,由Windows下的PC机写入的数据流可以被运行Solaris的Sun SPARC读取。

我们还可以使用数据流来读取/写入原始的未编码二进制数据。如果你想要一个“解析”输入流,请参阅QTextStream。
QDataStream类实现了c++基本数据类型的序列化,如char、short、int、char *等。

更复杂数据的序列化是通过将数据分解成基本单元来完成的。

数据流与QIODevice紧密配合。QIODevice表示一种输入/输出介质,可以从中读取数据和向其中写入数据,像文件,socket之类的在QT里都是封装的QIODevice,意味着我们保存在文件里数据同样可以发送到网络中去。

QFile类是I/O设备的一个示例。
示例(将二进制数据写入流):

  QFile file("file.dat");file.open(QIODevice::WriteOnly);QDataStream out(&file);   // we will serialize the data into the fileout << QString("the answer is");   // serialize a stringout << (qint32)42;        // serialize an integer

示例(从流中读取二进制数据):

  QFile file("file.dat");file.open(QIODevice::ReadOnly);QDataStream in(&file);    // read the data serialized from the fileQString str;qint32 a;in >> str >> a;           // extract "the answer is" and 42

写入流的每个项都以预定义的二进制格式写入,该格式根据项的类型而变化。支持的Qt类型包括QBrush, QColor, QDateTime, QFont, QPixmap, QString, QVariant和许多其他。Qt支持所有的类型看后面的备注

对于整数,最好总是将其转换为Qt整数类型进行写入,并将其读回相同的Qt整数类型。这可以确保我们获得所需大小的整数,并使我们免受编译器和平台差异的影响。

枚举可以通过QDataStream序列化,而不需要手动定义流操作符。枚举类使用声明的大小进行序列化。

举个例子,一个char *字符串被写成一个32位整数,等于包含’\0’字节的字符串的长度,后跟包含’\0’字节的字符串的所有字符。当读取char *字符串时,读取4个字节以创建32位长度值,然后读取char *字符串的许多字符,包括’\0’结束符。

初始I/O设备通常在构造函数中设置,但可以使用setDevice()进行更改。如果我们已经到达数据的末尾(或者如果没有I/O设备集),atEnd()将返回true。

三、版本控制

QDataStream的二进制格式自Qt 1.0以来一直在发展,并且可能会继续发展以反映Qt中的变化。当输入或输出复杂类型时,确保读取和写入使用相同版本的流(version())是非常重要的。如果你需要向前和向后兼容,你可以在应用程序中硬编码版本号:

stream.setVersion(QDataStream::Qt_4_0);

如果我们正在生成一种新的二进制数据格式,例如应用程序创建的文档的文件格式,那么我们可以使用QDataStream以可移植格式写入数据。通常,我们会编写一个简短的头文件,其中包含一个魔术字符串和一个版本号,以便为将来的扩展提供空间。例如:

  QFile file("file.xxx");file.open(QIODevice::WriteOnly);QDataStream out(&file);// Write a header with a "magic number" and a versionout << (quint32)0xA0B0C0D0;out << (qint32)123;out.setVersion(QDataStream::Qt_4_0);// Write the dataout << lots_of_interesting_data;

然后这样读:

  QFile file("file.xxx");file.open(QIODevice::ReadOnly);QDataStream in(&file);// Read and check the headerquint32 magic;in >> magic;if (magic != 0xA0B0C0D0)return XXX_BAD_FILE_FORMAT;// Read the versionqint32 version;in >> version;if (version < 100)return XXX_BAD_FILE_TOO_OLD;if (version > 123)return XXX_BAD_FILE_TOO_NEW;if (version <= 110)in.setVersion(QDataStream::Qt_3_2);elsein.setVersion(QDataStream::Qt_4_0);// Read the datain >> lots_of_interesting_data;if (version >= 120)in >> data_new_in_XXX_version_1_2;in >> other_interesting_data;

我们可以在序列化数据时选择要使用的字节顺序。默认设置是大端(MSB优先)。将其更改为小端序会破坏可移植性(除非阅读器也更改为小端序)。我们建议保持此设置,除非我们有特殊要求。

四、读取和写入原始二进制数据

我们可能希望直接从数据流中读取/写入自己的原始二进制数据。可以使用readRawData()将数据从流读入预分配的char *。类似地,可以使用writeRawData()将数据写入流。请注意,数据的任何编码/解码都必须由我们完成。

类似的一对函数是readBytes()和writeBytes()。它们与原始版本的区别如下:readBytes()读取一个quint32,它被认为是要读取的数据的长度,然后将该字节数读入预分配的char *;writeBytes()写入一个包含数据长度的quint32,后面跟着数据。请注意,数据的任何编码/解码(除了长度quint32)都必须由我们完成。

五、读写Qt集合类

Qt容器类也可以序列化为QDataStream。这些包括QList, QLinkedList, QVector, QSet, QHash和QMap。流操作符被声明为类的非成员。

六、读写其他Qt类

除了这里记录的重载流操作符,任何你可能想要序列化到QDataStream的Qt类都将有适当的流操作符声明为类的非成员:

QDataStream &operator<<(QDataStream &, const QXxx &);
QDataStream &operator>>(QDataStream &, QXxx &);

例如,以下是声明为QImage类非成员的流操作符:

QDataStream & operator<< (QDataStream& stream, const QImage& image);
QDataStream & operator>> (QDataStream& stream, QImage& image);

要查看我们喜欢的Qt类是否定义了类似的流操作符,请查看该类文档页面的相关非成员部分。

例如我们去读写自定义的结构体对象

//xx.h
//定义结构体
typedef struct st_mydata
{st_mydata() {};bool sex;int id;int age;QString address;QList<QString> extraInfo;friend QDataStream & operator<<(QDataStream &stream, const struct st_mydata & info);friend QDataStream & operator>>(QDataStream &stream,  struct st_mydata &info);
} ST_MYDATA;//xx.cpp
QDataStream & operator<<(QDataStream &stream, const struct st_mydata & info)
{stream <<info.id;stream <<info.age;stream <<info.sex;stream <<info.address;stream <<info.extraInfo;return stream;
}QDataStream & operator>>(QDataStream &stream, struct st_mydata &info)
{stream >>info.id;stream >>info.age;stream >>info.sex;stream >>info.address;stream >>info.extraInfo;return stream;
}// 用文件使用--写
void SerialDateUse::testWriteSelfStruct()
{ST_MYDATA data;data.id = 101;data.age = 18;data.sex = false;data.address = "四川省成都市双流区一号";data.extraInfo = QList<QString>{"好人", "非常熬", "牛逼嘞"};QFile file("xxxx.bin");file.open(QIODevice::WriteOnly);QDataStream out(&file);out<<data;file.flush();file.close();
}void SerialDateUse::testReadSelfStruct()
{ST_MYDATA data;QFile file("xxxx.bin");if(! file.open(QIODevice::ReadOnly))return;QDataStream in(&file);in>>data;qDebug()<<"[Info]: ----------------- ";qDebug()<<"id: "<<data.id;qDebug()<<"age: "<<data.age;qDebug()<<"sex: "<<(data.sex ? QString("female") : QString("male"));qDebug()<<"address: "<<data.address;qDebug()<<"extra info:"<<data.extraInfo.join(",");
}
/* 输入如下
[Info]: -----------------
id:  101
age:  18
sex:  "male"
address:  "四川省成都市双流区一号"
extra info: "好人,非常熬,牛逼嘞"
*/

读写对象也和结构体一样

class SerialDateUse:QObject
{Q_OBJECT
public:SerialDateUse();friend QDataStream & operator<< (QDataStream& stream, const SerialDateUse& info);friend QDataStream & operator>> (QDataStream& stream, SerialDateUse& info);int age() const;void setAge(int newAge);QString name() const;void setName(const QString &newName);QList<QString> infos() const;void setInfos(const QList<QString> &newInfos);private:int m_age = 0;QString m_name = "None";QList<QString> m_infos;
};
#include "SerialDateUse.h"SerialDateUse::SerialDateUse()
{}int SerialDateUse::age() const
{return m_age;
}void SerialDateUse::setAge(int newAge)
{m_age = newAge;
}QString SerialDateUse::name() const
{return m_name;
}void SerialDateUse::setName(const QString &newName)
{m_name = newName;
}QList<QString> SerialDateUse::infos() const
{return m_infos;
}void SerialDateUse::setInfos(const QList<QString> &newInfos)
{m_infos = newInfos;
}QDataStream &operator<<(QDataStream &stream, const SerialDateUse &info)
{stream<<info.m_age;stream<<info.m_name;stream<<info.m_infos;return stream;
}QDataStream &operator>>(QDataStream &stream, SerialDateUse &info)
{stream>>info.m_age;stream>>info.m_name;stream>>info.m_infos;return stream;
}

使用代码

//写数据
SerialDateUse ss;
ss.setAge(12);
ss.setName("张洪源");
ss.setInfos(QList<QString>{("xxx"), ("YYY")});QFile file("file.bin");
if (!file.open(QIODevice::WriteOnly))
{qDebug()<<"File Write Error";
}
QDataStream out(&file);out<<ss;
file.flush();
file.close();//读数据
SerialDateUse read;QFile readfile("file.bin");
if (!readfile.open(QIODevice::ReadOnly))
{qDebug()<<"File Read Error";
}
QDataStream read_out(&readfile);read_out>>read;qDebug()<<read.age();
qDebug()<<read.name();
qDebug()<<read.infos();
readfile.close();

七、使用读事务

当数据流在异步设备上运行时,数据块可以在任意时间点到达。QDataStream类实现了一种事务机制,该机制提供了使用一系列流操作符自动读取数据的能力。例如,你可以通过使用连接到readyRead()信号的插槽中的事务来处理来自套接字的不完整读取:

  in.startTransaction();QString str;qint32 a;in >> str >> a; // try to read packet atomicallyif (!in.commitTransaction())return;     // wait for more data

如果没有接收到完整的数据包,该代码将流恢复到初始位置,之后需要等待更多的数据到达。

八、Qt支持的序列化类型

QDataStream类允许我们序列化本节中列出的从版本18开始的Qt数据类型。
在读写时,最好将整数转换为Qt整数类型,如qint16或quint32。这确保了无论应用程序碰巧运行在什么底层平台和体系结构上,我们始终确切地知道正在读取和写入的整数的大小,确实因为不同平台的 的分配大小还是有区分的。

  • bool
  • qint8
  • qint16
  • qint32
  • qint64
  • quint8
  • quint16
  • quint32
  • quint64
  • float
  • double
  • const char *
  • QBitArray
  • QBrush
  • QByteArray
  • QColor
  • QCursor
  • QDate
  • QDateTime
  • QEasingCurve
  • QFont
  • QGenericMatrix
  • QHash<Key, T>
  • QIcon
  • QImage
  • QKeySequence
  • QLinkedList
  • QList
  • QMap<Key, T>
  • QMargins
  • QMatrix4x4
  • QPair<T1, T2>
  • QPalette
  • QPen
  • QPicture
  • QPixmap
  • QPoint
  • QQuaternion
  • QRect
  • QRegExp
  • QRegularExpression
  • QRegion
  • QSize
  • QString
  • QTime
  • QTransform
  • QUrl
  • QVariant
  • QVector2D
  • QVector3D
  • QVector4D
  • QVector

这篇关于Qt扫盲-QDataStream 序列化和反序列化理论的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/232514

相关文章

2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题

题库来源:安全生产模拟考试一点通公众号小程序 2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题是由安全生产模拟考试一点通提供,流动式起重机司机证模拟考试题库是根据流动式起重机司机最新版教材,流动式起重机司机大纲整理而成(含2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题参考答案和部分工种参考解析),掌握本资料和学校方法,考试容易。流动式起重机司机考试技

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点: 鼠标左键双击,设定红色的起点。左键双击设定起点,用红色标记。 设定终点: 鼠标右键双击,设定蓝色的终点。右键双击设定终点,用蓝色标记。 设置障碍点: 鼠标左键或者右键按着不放,拖动可以设置黑色的障碍点。按住左键或右键并拖动,设置一系列黑色障碍点

使用Qt编程QtNetwork无法使用

使用 VS 构建 Qt 项目时 QtNetwork 无法使用的问题 - 摘叶飞镖 - 博客园 (cnblogs.com) 另外,强烈建议在使用QNetworkAccessManager之前看看这篇文章: Qt 之 QNetworkAccessManager踏坑记录-CSDN博客 C++ Qt开发:QNetworkAccessManager网络接口组件 阅读目录 1.1 通用API函数

Qt多语种开发教程

Qt作为跨平台的开发工具,早已应用到各行各业的软件开发中。 今天讲讲,Qt开发的正序怎么做多语言开发。就是说,你设置中文,就中文显示;设置英语就英文显示,设置繁体就繁体显示,设置发育就显示法语等。 开发环境(其实多语种这块根环境没太大关系):win10,Qt.5.12.10 一.先用QtCreator创建一个简单的桌面程序 1.工程就随便命名“LanguageTest”,其他默认。 2.在设计师

Qt中window frame的影响

window frame 在创建图形化界面的时候,会创建窗口主体,上面会多出一条,周围多次一圈细边,这就叫window frame窗口框架,这是操作系统自带的。 这个对geometry的一些属性有一定影响,主要体现在Qt坐标系体系: 窗口当中包含一个按钮,这个按钮的坐标系是以父元素为参考,那么这个参考是widget本体作为参考,还是window frame作为参考,这两种参考体系都存在

【Qt】定时器事件

定时器事件 在之前学习QTimer中实现了定时器的功能,而在QTimer背后是QTimerEvent定时器事件进行支撑的。在QObject中提供了一个timeEvent这个函数。 startTimer启动定时器killTimer关闭定时器 Qt 中在进⾏窗⼝程序的处理过程中,经常要周期性的执⾏某些操作,或者制作⼀些动画效果,使⽤定 时器就可以实现。所谓定时器就是在间隔⼀定时间后,去执⾏某⼀

QT 编译报错:C3861: ‘tr‘ identifier not found

问题: QT 编译报错:C3861: ‘tr’ identifier not found 原因 使用tr的地方所在的类没有继承自 QObject 类 或者在不在某一类中, 解决方案 就直接用类名引用 :QObject::tr( )