赞
踩
序列化: 指的是将一个内存对象转化成一串字节数据(存储在一个字节数组中),可用于保存到本地文件或网络传输。
反序列化: 就是将字节数据还原成内存对象。
序列化是将对象转换为字节流的过程,可以将对象持久化保存在磁盘或者通过网络传输。
反序列化是将字节流转换为对象的过程,可以将保存或传输的对象重新恢复到内存中。
序列化和反序列化的作用包括:
总之,序列化和反序列化在很多场景中都是非常有用的,可以将对象转化为可以存储、传输或共享的格式,以便在需要时可以重新加载和使用。
在Qt官方里提供了一个这样的工具,就是 QDataStream ,注意这里不是Json格式的序列化。
Json文本转Qt对象的可以参考
https://github.com/smurfomen/QSerializer
数据流是编码信息的二进制流,它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 file
out << QString("the answer is"); // serialize a string
out << (qint32)42; // serialize an integer
示例(从流中读取二进制数据):
QFile file("file.dat");
file.open(QIODevice::ReadOnly);
QDataStream in(&file); // read the data serialized from the file
QString 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 version
out << (quint32)0xA0B0C0D0;
out << (qint32)123;
out.setVersion(QDataStream::Qt_4_0);
// Write the data
out << lots_of_interesting_data;
然后这样读:
QFile file("file.xxx"); file.open(QIODevice::ReadOnly); QDataStream in(&file); // Read and check the header quint32 magic; in >> magic; if (magic != 0xA0B0C0D0) return XXX_BAD_FILE_FORMAT; // Read the version qint32 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); else in.setVersion(QDataStream::Qt_4_0); // Read the data in >> 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容器类也可以序列化为QDataStream。这些包括QList, QLinkedList, QVector, QSet, QHash和QMap。流操作符被声明为类的非成员。
除了这里记录的重载流操作符,任何你可能想要序列化到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 atomically
if (!in.commitTransaction())
return; // wait for more data
如果没有接收到完整的数据包,该代码将流恢复到初始位置,之后需要等待更多的数据到达。
QDataStream类允许我们序列化本节中列出的从版本18开始的Qt数据类型。
在读写时,最好将整数转换为Qt整数类型,如qint16或quint32。这确保了无论应用程序碰巧运行在什么底层平台和体系结构上,我们始终确切地知道正在读取和写入的整数的大小,确实因为不同平台的 的分配大小还是有区分的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。