4人参与 • 2025-12-09 • C/C++
在c++应用程序中,经常会涉及到对一些数据进行序列化和反序列化的处理。序列化可以将一个对象转换为一串字节流,这样就可以将其存储在硬盘上或者通过网络传输到其他设备上。而反序列化则是将这些字节流解析成原始的对象。
在qt中,数据的序列化和反序列化可以使用qdatastream类来完成。qdatastream是一个方便的qt类,它可以将基本数据类型、qt数据类型以及用户定义的数据类型都进行序列化和反序列化。在使用qdatastream进行序列化时,需要指定一个qiodevice类的子类(例如qfile或qbuffer)来将数据写入到文件或者内存中。
示例如下:
//【1】结构体
typedef struct _stctrlcmdheader
{
unsigned char rctrlobj;
unsigned char rctrlobjid;
unsigned char rctrlcmd;
unsigned short rdatalen;
public:
_stctrlcmdheader()
{
memset(this, 0, sizeof(_stctrlcmdheader));
rctrlobj = 0x01;
rctrlobjid = 0x00;
}
friend qdatastream& operator<<(qdatastream& datastream, const _stctrlcmdheader& data) //序列化
{
datastream << data.rctrlobj;
datastream << data.rctrlobjid;
datastream << data.rctrlcmd;
datastream << data.rdatalen;
return datastream;
}
friend qdatastream& operator>>(qdatastream& datastream, _stctrlcmdheader& data) //反序列化
{
datastream >> data.rctrlobj;
datastream >> data.rctrlobjid;
datastream >> data.rctrlcmd;
datastream >> data.rdatalen;
return datastream;
}
}stctrlcmdheader;
//【2】序列化到qbytearray
qbytearray data;
qdatastream out(&data, qiodevice::writeonly); // we will serialize the data into the data
datastream.setbyteorder(qdatastream::littleendian); //设置小端对齐
out << qstring("the answer is"); // serialize a string
out << (qint32)42; // serialize an integer
stctrlcmdheader header;
//...
out << header; //serialize struct or class
//【3】反序列化
char szbuffer[1024] = { 0 };
int len = 0 ;
//...获取网络数据
qbytearray byarray(szbuffer, len);
qdatastream datastream(&byarray, qiodevice::readonly);
datastream.setbyteorder(qdatastream::littleendian);
char sztext[20] = { 0 };
datastream.readrawdata((char*)sztext, 10); //unserialize a string
qint32 number = 0;
datastream >> number ; //unserialize an integer
stctrlcmdheader header;
//...
out >> header; //unserialize struct or class
在qt中很方便地可以用到这些类,但是脱离qt,在标准的c++标准库就没有这么方便了,今天就模仿qbytearray和qdatastream类的接口,写了序列化和反序列化的类cbytearray和cdatastream,可以无缝替换qbytearray和qdatastream,下面就来介绍一下cbytearray和cdatastream的设计和实现。
设计要点:
1) cbytearray可以理解是纯字节数组,可以存放任意格式的数据,于是后台用 std::basic_string<char>来存储实际的数据。
2)构造函数接收字符数组(char*+len)
3) 构造函数接受右值引用,实现快速的移动语义
4)重载符号=,接受右值引用,实现快速的移动语义
5)根据长度写入和读取原始数据接口
int writerawdata(const char* data, puint32 size) override; int readrawdata(char* datsa, puint32 size) override;
6) 整个类采用pimpl技法实现
具体实现:
类型定义:datatype.h
#ifndef _data_type_h_ #define _data_type_h_ typedef char pint8; typedef unsigned char puint8; typedef short pint16; typedef unsigned short puint16; typedef int pint32; typedef unsigned int puint32; typedef long long pint64; typedef unsigned long long puint64; typedef float pfloat32; typedef double pfloat64; #endif
抽象接口定义如下:idatabuffer.h
#ifndef _idata_buffer_h_
#define _idata_buffer_h_
#include "datatype.h"
class idatabuffer
{
public:
virtual ~idatabuffer(){}
virtual int writebytes(const char*, puint32) = 0;
virtual int readbytes(char*, puint32) = 0;
virtual int writerawdata(const char*, puint32) = 0;
virtual int readrawdata(char*, puint32) = 0;
virtual int bytesavailable() const = 0;
virtual puint64 skip(puint64 maxsize) = 0;
};
#endif
bytearray.h
#ifndef _byte_array_h_
#define _byte_array_h_
#include <string>
#include <memory>
#include "idatabuffer.h"
class cbytearrayprivate;
class cbytearray : public idatabuffer
{
public:
explicit cbytearray();
cbytearray(const cbytearray& other);
cbytearray(cbytearray&& other);
cbytearray(const char* buffer, int size);
cbytearray& operator=(cbytearray&& other);
operator bool() const;
virtual ~cbytearray();
public:
int writerawdata(const char* data, puint32 size) override;
int readrawdata(char* datsa, puint32 size) override;
int writebytes(const char*, puint32) override;
int readbytes(char*, puint32) override;
const char* data() const;
int size() const;
int bytesavailable() const override;
puint64 skip(puint64 maxsize) override;
cbytearray& append(const char* data, puint32 size);
cbytearray& append(const char* data);
void clear();
private:
std::unique_ptr<cbytearrayprivate> d_ptr;
};
#endif
bytearray.cpp
#include "bytearray.h"
#include <assert.h>
#include <string.h>
//#include <string_view>
class cbytearrayprivate
{
public:
explicit cbytearrayprivate():m_readpos(0) {}
cbytearrayprivate(const char* buffer, int size): m_buffer(buffer, size), m_readpos(0) {}
//这个类,有点类似于golang语言中的切片slice。这就意味着std::string_view本身并不拥有内存本身,
//它只是一个view,一个窗口,观看内存的窗口
//std::string_view m_buffer;
std::basic_string<char> m_buffer;
int m_readpos; //暂时没用
};
cbytearray::cbytearray()
: d_ptr(new cbytearrayprivate)
{
}
cbytearray::cbytearray(const cbytearray& other)
: d_ptr(new cbytearrayprivate)
{
d_ptr->m_buffer = other.d_ptr->m_buffer;
d_ptr->m_readpos = other.d_ptr->m_readpos;
}
cbytearray::cbytearray(cbytearray&& other)
: d_ptr(other.d_ptr.release())
{
other.d_ptr.reset(new cbytearrayprivate);
}
cbytearray::cbytearray(const char* buffer, int size)
: d_ptr(new cbytearrayprivate(buffer,size))
{
}
cbytearray::operator bool() const
{
return (0 ==size());
}
cbytearray& cbytearray::operator=(cbytearray&& other)
{
if (d_ptr == other.d_ptr){
return *this;
}
cbytearrayprivate* temp = d_ptr.release();
d_ptr.reset(other.d_ptr.release());
other.d_ptr.reset(temp);
return *this;
}
cbytearray::~cbytearray()
{
}
int cbytearray::writerawdata(const char* data, puint32 size)
{
d_ptr->m_buffer.append(data, size);
return size;
}
int cbytearray::readrawdata(char* data, puint32 size)
{
if (d_ptr->m_buffer.copy(data, size, 0) != size){
assert(0);
return 0;
}
d_ptr->m_buffer.erase(0, size);
return size;
}
const char* cbytearray::data() const
{
return d_ptr->m_buffer.data();
}
int cbytearray::size() const
{
return d_ptr->m_buffer.size();
}
int cbytearray::bytesavailable() const
{
return size();
}
puint64 cbytearray::skip(puint64 maxsize)
{
puint64 nskip = maxsize;
if (maxsize >= d_ptr->m_buffer.size()){
nskip = d_ptr->m_buffer.size();
d_ptr->m_buffer.clear();
return nskip;
}else{
d_ptr->m_buffer.erase(0, maxsize);
return nskip;
}
}
int cbytearray::writebytes(const char*, puint32)
{
assert(0);
return 0;
}
int cbytearray::readbytes(char*, puint32)
{
assert(0);
return 0;
}
cbytearray& cbytearray::append(const char* data, puint32 size)
{
d_ptr->m_buffer.append(data, size);
return *this;
}
cbytearray& cbytearray::append(const char* data)
{
if (!data){
return *this;
}
int len = strlen(data);
return append(data, len);
}
void cbytearray::clear() {
d_ptr->m_buffer.clear();
}
设计要点:
1) 支持所有支持idatabuffer的实现类
2)支持c++的基本数据类型序列化和反序列化,重载operator<<和operator>>
3) 支持自定义数据类型的序列化和反序列化,如结构体或类
4)反序列化时,可以忽略指定的字节
5)反序列化可变字节长度时,可以获取未序列化的数据长度,用户获取长度申请空间并反序列化到指定的内存中
具体实现:
shortwavetoolfun.h
#ifndef _short_wave_tool_func_h_
#define _short_wave_tool_func_h_
#include <string>
#include "datatype.h"
#include <functional>
#include <memory>
#include <string.h>
#define work_msg_id(link, type, sub) ( (link<<16) | (type<<8) | sub )
#define get_work_type(id) ((id >> 8) & 0xff)
#define get_work_cmd(id) (id & 0xff)
#define get_work_b_type(id) ((id >> 16) & 0xff)
#define member_off_set(type, member) ((size_t) &(static_cast<type*>(0))->member)
#define container_of(ptr, type, member) ({ \
const typeof(((type*)0)->member) * __mptr = (ptr); \
(type*)((char*)__mptr - member_off_set(type,member)); })
template <typename t, class = typename std::enable_if<sizeof(t)%2==0>::type>
t reversenumber(t value)
{
static_assert(sizeof(t)%2==0, "t type is invalid!!!");
union x{
t val1;
char val2[sizeof(t)];
};
int i = 0;
int size = sizeof(t);
char temp = 0;
x x;
x.val1 = value;
for (i = 0; i < sizeof(t)/2; i++){
temp = x.val2[i];
x.val2[i] = x.val2[size-i-1];
x.val2[size-i-1] = temp;
}
return x.val1;
}
template <typename t, class = typename std::enable_if<sizeof(t)<=sizeof(puint64)>::type>
puint64 transttopuint64(t value)
{
static_assert(sizeof(t)<=sizeof(puint64), "t type is invalid!!!");
union x{
t val1;
puint64 val2;
};
x x;
x.val2 = 0; //将高位置0
x.val1 = value;
return x.val2;
}
template <typename t, class = typename std::enable_if<sizeof(t)<=sizeof(puint64)>::type>
t transpuint64tot(puint64 value)
{
static_assert(sizeof(t)<=sizeof(puint64), "t type is invalid!!!");
union x{
t val1;
puint64 val2;
};
x x;
x.val2 = value;
return x.val1;
}
#endif
datastream.h
#ifndef _data_stream_h_
#define _data_stream_h_
#include "datatype.h"
class idatabuffer;
class cbytearray;
class cdatastream
{
public:
explicit cdatastream(idatabuffer* pbuffer);
~cdatastream();
bool bigendian() const;
void setbigendian(bool newbigendian);
cdatastream& operator<<(bool i);
cdatastream& operator<<(pint8 i);
cdatastream& operator<<(puint8 i);
cdatastream& operator<<(pint16 i);
cdatastream& operator<<(puint16 i);
cdatastream& operator<<(pint32 i);
cdatastream& operator<<(puint32 i);
cdatastream& operator<<(pint64 i);
cdatastream& operator<<(puint64 i);
cdatastream& operator<<(pfloat32 i);
cdatastream& operator<<(pfloat64 i);
cdatastream& operator<<(const char* i);
cdatastream& operator<<(const cbytearray& data);
template <typename t>
cdatastream& operator<<(const t& t)
{
(*this) << t;
return *this;
}
cdatastream& operator>>(bool& i);
cdatastream& operator>>(pint8& i);
cdatastream& operator>>(puint8& i);
cdatastream& operator>>(pint16& i);
cdatastream& operator>>(puint16& i);
cdatastream& operator>>(pint32& i);
cdatastream& operator>>(puint32& i);
cdatastream& operator>>(pint64& i);
cdatastream& operator>>(puint64& i);
cdatastream& operator>>(pfloat32& i);
cdatastream& operator>>(pfloat64& i);
cdatastream& operator>>(char*& i);
cdatastream& operator>>(cbytearray& data);
template <typename t>
cdatastream& operator>>(t& t)
{
(*this) >> t;
return *this;
}
int writerawdata(const char* data, puint32 size);
int readrawdata(char* data, puint32 size);
bool atend() const;
int bytesavailable() const;
int skiprawdata(int len);
private:
bool m_bigendian;
idatabuffer* m_buffer;
};
#endif
datastream.cpp
#include "idatabuffer.h"
#include "datastream.h"
#include "shortwavetoolfun.h"
#include <string.h>
#include "bytearray.h"
cdatastream::cdatastream(idatabuffer* pbuffer)
: m_bigendian(false)
, m_buffer(pbuffer)
{
}
cdatastream::~cdatastream()
{
}
bool cdatastream::bigendian() const
{
return m_bigendian;
}
void cdatastream::setbigendian(bool newbigendian)
{
m_bigendian = newbigendian;
}
cdatastream& cdatastream::operator<<(bool i)
{
*this << (pint8)i;
return *this;
}
cdatastream& cdatastream::operator<<(pint8 i)
{
pint8 ch = i;
m_buffer->writerawdata(&ch, sizeof(ch));
return *this;
}
cdatastream& cdatastream::operator<<(puint8 i)
{
pint8 ch = i;
m_buffer->writerawdata(&ch, sizeof(ch));
return *this;
}
cdatastream& cdatastream::operator<<(pint16 i)
{
pint16 temp = i;
if (m_bigendian){
temp = reversenumber(i);
}
m_buffer->writerawdata((char*)&temp, sizeof(temp));
return *this;
}
cdatastream& cdatastream::operator<<(puint16 i)
{
puint16 temp = i;
if (m_bigendian){
temp = reversenumber(i);
}
m_buffer->writerawdata((char*)&temp, sizeof(temp));
return *this;
}
cdatastream& cdatastream::operator<<(pint32 i)
{
pint32 temp = i;
if (m_bigendian){
temp = reversenumber(i);
}
m_buffer->writerawdata((char*)&temp, sizeof(temp));
return *this;
}
cdatastream& cdatastream::operator<<(puint32 i)
{
puint32 temp = i;
if (m_bigendian){
temp = reversenumber(i);
}
m_buffer->writerawdata((char*)&temp, sizeof(temp));
return *this;
}
cdatastream& cdatastream::operator<<(pint64 i)
{
pint64 temp = i;
if (m_bigendian){
temp = reversenumber(i);
}
m_buffer->writerawdata((char*)&temp, sizeof(temp));
return *this;
}
cdatastream& cdatastream::operator<<(puint64 i)
{
puint64 temp = i;
if (m_bigendian){
temp = reversenumber(i);
}
m_buffer->writerawdata((char*)&temp, sizeof(temp));
return *this;
}
cdatastream& cdatastream::operator<<(pfloat32 i)
{
pfloat32 temp = i;
if (m_bigendian){
union {
float val1;
puint32 val2;
} x;
x.val1 = i;
x.val2 = reversenumber(x.val2);
m_buffer->writerawdata((char*)&x.val2, sizeof(i));
}else{
m_buffer->writerawdata((char*)&temp, sizeof(i));
}
return *this;
}
cdatastream& cdatastream::operator<<(pfloat64 i)
{
pfloat64 temp = i;
if (m_bigendian){
union {
double val1;
puint64 val2;
} x;
x.val1 = i;
x.val2 = reversenumber(x.val2);
m_buffer->writerawdata((char*)&x.val2, sizeof(i));
}else{
m_buffer->writerawdata((char*)&temp, sizeof(i));
}
return *this;
}
cdatastream& cdatastream::operator<<(const char* i)
{
if (!i) {
*this << (puint32)0;
return *this;
}
int len = strlen(i) + 1;
*this << (puint32)len;
m_buffer->writerawdata(i, len);
return *this;
}
cdatastream& cdatastream::operator>>(bool& i)
{
pint8 v;
*this >> v;
i = !!v;
return *this;
}
cdatastream& cdatastream::operator>>(pint8& i)
{
m_buffer->readrawdata(&i, 1);
return *this;
}
cdatastream& cdatastream::operator>>(puint8& i)
{
m_buffer->readrawdata(reinterpret_cast<char *>(&i), sizeof(i));
return *this;
}
cdatastream& cdatastream::operator>>(pint16& i)
{
m_buffer->readrawdata((char *)(&i), sizeof(i));
if (m_bigendian){
i = reversenumber(i);
}
return *this;
}
cdatastream& cdatastream::operator>>(puint16& i)
{
m_buffer->readrawdata(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigendian){
i = reversenumber(i);
}
return *this;
}
cdatastream& cdatastream::operator>>(pint32& i)
{
m_buffer->readrawdata((char *)(&i), sizeof(i));
return *this;
}
cdatastream& cdatastream::operator>>(puint32& i)
{
m_buffer->readrawdata(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigendian){
i = reversenumber(i);
}
return *this;
}
cdatastream& cdatastream::operator>>(pint64& i)
{
m_buffer->readrawdata((char *)(&i), sizeof(i));
if (m_bigendian){
i = reversenumber(i);
}
return *this;
}
cdatastream& cdatastream::operator>>(puint64& i)
{
m_buffer->readrawdata(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigendian){
i = reversenumber(i);
}
return *this;
}
cdatastream& cdatastream::operator>>(pfloat32& i)
{
i = 0.0f;
m_buffer->readrawdata(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigendian){
union {
float val1;
puint32 val2;
} x;
x.val2 = reversenumber(*reinterpret_cast<puint32 *>(&i));
i = x.val1;
}
return *this;
}
cdatastream& cdatastream::operator>>(pfloat64& i)
{
i = 0.0f;
m_buffer->readrawdata(reinterpret_cast<char *>(&i), sizeof(i));
if (m_bigendian){
union {
double val1;
puint64 val2;
} x;
x.val2 = reversenumber(*reinterpret_cast<puint64 *>(&i));
i = x.val1;
}
return *this;
}
cdatastream& cdatastream::operator>>(char*& i)
{
puint32 len = 0;
*this >> len;
if (len == 0)
return *this;
m_buffer->readrawdata(i, len);
i[len] = '\0';
return *this;
}
cdatastream& cdatastream::operator<<(const cbytearray& data)
{
m_buffer->writerawdata(data.data(), data.size());
return *this;
}
cdatastream& cdatastream::operator>>(cbytearray& data)
{
puint32 len = 0;
*this >> len;
if (len == 0)
return *this;
std::unique_ptr<char[]> pbuffer(new char[len]);
m_buffer->readrawdata(pbuffer.get(), len);
data.append(pbuffer.get(), len);
return *this;
}
int cdatastream::writerawdata(const char* data, puint32 size)
{
return m_buffer->writerawdata(data, size);
}
int cdatastream::readrawdata(char* data, puint32 size)
{
return m_buffer->readrawdata(data, size);
}
bool cdatastream::atend() const
{
return m_buffer->bytesavailable() <= 0;
}
int cdatastream::bytesavailable() const
{
return m_buffer->bytesavailable();
}
int cdatastream::skiprawdata(int len)
{
return m_buffer->skip(len);
}结构体定义:
//传输的整个报文
typedef struct _stshortwavepacket
{
//共用
puint32 contextid;
void* pappdata; //正文数据
puint16 frameindex;
stobjectflag srcobjmark;
stobjectflag destobjmark;
puint8 ismustresponse;
puint8 responsestatus;
public:
_stshortwavepacket() {
}
}stshortwavepacket;
using stchannelworkparamdata = kvdata<puint16, puint64>;
using channelworkparamdatacontainer = std::vector<std::shared_ptr<stchannelworkparamdata>>;
//结构体定义
template <bool ishavetype>
struct stchannelworkparam
{
puint16 signaltype; //类别
channelworkparamdatacontainer workparamdata;
public:
stchannelworkparam() {
signaltype = 0;
}
stchannelworkparam(const stchannelworkparam&) = delete;
stchannelworkparam& operator=(const stchannelworkparam&) = delete;
puint16 getdatasize() {
puint16 size = ishavetype ? 2 : 0;
for (auto& it : workparamdata) {
size += it->getsize();
}
return size;
}
std::string tostring() const {
return std::string("type:") + numconvertstring(signaltype);
}
friend cdatastream& operator<<(cdatastream& datastream, const stchannelworkparam& data)
{
if (ishavetype) {
datastream << data.signaltype;
}
for (auto& it : data.workparamdata) {
it->serializedata(datastream);
}
return datastream;
}
friend cdatastream& operator>>(cdatastream& datastream, stchannelworkparam& data)
{
if (ishavetype) {
datastream >> data.signaltype;
}
while (datastream.bytesavailable() > 6) {
std::shared_ptr<stchannelworkparamdata> pdata = std::make_shared<stchannelworkparamdata>();
pdata->unserializedata(datastream);
data.workparamdata.push_back(pdata);
}
return datastream;
}
};
using stsendchannelworkparam = stchannelworkparam<true>;
using stuploadregisterstatus = stchannelworkparam<false>;序列化数据:
template <puint32 type, typename sttype>
int encodedata(char* data, int& len, const stshortwavepacket* ppacket)
{
puint16 crccode = 0;
cbytearray byarray;
cdatastream datastream(&byarray);
sttype* pmessage = reinterpret_cast<sttype*>(ppacket->pappdata);
if (pmessage == nullptr)
return 1;
//[1]
datastream.writerawdata((const char*)m_startflag, sizeof(m_startflag));
datastream << getframeno();
datastream << ppacket->srcobjmark.data;
datastream << ppacket->destobjmark.data;
datastream << ppacket->ismustresponse;
datastream << (puint8)get_work_type(type);
datastream << pmessage->getdatasize();
//[2]
datastream << (*pmessage);
crccode = getcrccode(byarray.data(), byarray.size());
datastream << crccode;
datastream.writerawdata((const char*)m_endflag, sizeof(m_endflag));
len = byarray.size();
memcpy(data, byarray.data(), len);
return 0;
}接收到网络数据,反序列化数据:
template <puint32 type, typename sttype>
int parsedata(const char* data, int len, stshortwavepacket* ppacket)
{
cbytearray byarray(data, len);
cdatastream datastream(&byarray);
puint16 length = 0;
ppacket->contextid = type;
datastream.skiprawdata(4); //忽略4个字节
datastream >> ppacket->frameindex;
m_frameno = ppacket->frameindex;
datastream >> ppacket->srcobjmark.data;
datastream >> ppacket->destobjmark.data;
datastream >> ppacket->ismustresponse; //是否应答
datastream.skiprawdata(1); //忽略功能指令,1个字节
datastream >> length;
if (length != sttype::getdatasize())
return -1;
std::unique_ptr<sttype> pappdata = std::make_unique<sttype>();
datastream >> (*pappdata);
ppacket->pappdata = pappdata.release();
return 0;
}到此这篇关于c++实现数据的序列化和反序列化详解的文章就介绍到这了,更多相关c++数据序列化和反序列化内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论