github連結:
https://github.com/PengChaoJay/CPP/tree/main/Serialization
概念
序列化 (serialization) 是将對象的狀态資訊轉化為可以存儲或傳輸的形式的過程。在序列化期間,對象将其目前狀态寫入臨時或持久性存儲區。以後可以通過從存儲區中讀取或反序列化對象的狀态,重新建立該對象。
序列化的方式
- 文本格式:JSON,XML
- 二進制格式:protobuf
二進制序列化
序列化: 将資料結構或對象轉換成二進制串的過程
反序列化:經在序列化過程中所産生的二進制串轉換成資料結構或對象的過程
- 序列化後,資料小,傳輸速度快 - 序列化、反序列化速度快
示範
1. 基本類型序列化、反序列化
基本類型序列化
int main()
{
DataStream ds;
int n =123;
double d = 23.2;
string s = "hellow serialization";
ds << n <<d <<s;
ds.save("a.out");
}
基本類型的反序列化
{
DataStream ds;
int n;
double d;
string s;
ds.load("a.load");
ds<<d<<s<<d;
std::cout<<n<<d<<s<<std::endl;
}
2.複合類型資料序列化、反序列化
複合類型資料序列化
int main()
{
std::vector<int>v{3,2,1};
std::map<string,string>m;
m["name"] = "kitty";
m["phone"] = "12121";
m["gender"] = "male";
DataStream ds;
ds<<v<<s;
ds.save("a.out");
}
複合類型資料反序列化
``` C++
int main()
{
DataStreawm ds;
ds.load("a.out");
std::vector<int>v;
std::map<string,string>m;
ds>>v>>m;
for(auto it = v.begin();it != v.end();it++)
{
std::cout<<*it<<std::endl;
}
for(auto it = m.begin();it!= m.end;it++)
{
std::cout<<it->first<<"="<<it->second<<std::endl;
}
}
3.自定義類的序列化、反序列化
自定義類的序列化
class A:public Serialization
{
public:
A();
~A();
A(const string & name,int age):m_name(name),m_age(age){}
void show()
{
std::cout<<m_name<<" "<<m_age<<std::endl;
}
//需要序列化的字段
SERIALIZE(m_name,m_age);
private:
string m_name;
int m_age;
}
int main()
{
A a("Hell",12);
DataStream ds;
ds<<a;
ds.save("a.out");
}
反序列化類的類型
int main()
{
DataStreawm ds;
ds.load("a.out");
std::vector<int>v;
std::map<string,string>m;
ds>>v>>m;
for(auto it = v.begin();it != v.end();it++)
{
std::cout<<*it<<std::endl;
}
for(auto it = m.begin();it!= m.end;it++)
{
std::cout<<it->first<<"="<<it->second<<std::endl;
}
}
3.自定義類的序列化、反序列化
自定義類的反序列化
class A:public Serialization
{
public:
A();
~A();
A(const string & name,int age):m_name(name),m_age(age){}
void show()
{
std::cout<<m_name<<" "<<m_age<<std::endl;
}
//需要序列化的字段
SERIALIZE(m_name,m_age);
private:
string m_name;
int m_age;
}
class B:public Serialization
{
public:
B();
~B();
void add(const A & a)
{
m_vector.add(a);
}
B(const string & name,int age):m_name(name),m_age(age){}
void show()
{
for(auto it = m_vector.begin();it! = m_vector.end();it++)
{
it->show();
}
}
//需要序列化的字段
SERIALIZE(m_vector);
private:
std::vector<A> m_vector;
}
int main()
{
// 序列化
// B b;
// b.add(A("hello",12));
// b.add(A("liuc",21));
// b.add(A("wang",34));
// DataSream ds;
// ds<<b;
// ds.save("a.out");
//反序列化
B b;
DataSream ds;
ds.load("a.out");
ds>>b;
b.show();
}
Protobuf 與 srialization的差別
protobuf | serialize | |
二進制格式 | 是 | 是 |
資料體積 | 小 | 小 |
編碼速度 | 快 | 快 |
資料格式類型 | 豐富 | 更加豐富 |
消息定義檔案 | 需要 | 不需要 |
需要編譯 | 需要 | 不需要 |
代碼實作 | 複雜 | 簡單 |
資料類型的定義
enum DataType
{
BOOL =0,
CHAR,
INT32,
INT64,
FLOAT,
DOUBLE,
STRING,
VECTOR,
LIST,
MAP,
SET,
CUSTOM
}
基本類型序列化+反序列化
基本資料類型編碼
字段類型 | 字段長度(位元組) | 底層編碼格式 |
bool | 2 | Type(1) + Value(1) |
char | 2 | Type(1) + Value(1) |
int32 | 5 | Type(1) + Value(4) |
int64 | 9 | Type(1) + Value(8) |
float | 5 | Type(1) + Value(4) |
double | 9 | Type(1) + Value(8) |
stirng | 可變長度 | Type(1) +Length(5) + Value(變成) |
對于string類型,1個位元組代表類型,長度用的是int32 | ||
### 複合類型序列化+ 反序列化 | ||
#### 複合資料類型編碼 | ||
字段類型 | 字段長度(位元組) | 底層編碼格式 |
:---: | :----: | :----: |
vector | 可變長 | Type(1) + length(5) + Value(T+T+T+...) |
list | 可變長 | Type(1) + length(5) + Value(T+T+T+...) |
map | 可變長 | Type(1) + length(5) + Value((k,v)+(k,v)+(k,v)+...) |
set | 可變長 | Type(1) + length(5) + Value(T+T+T+...) |
其中length代表的int32的表示的長度 | ||
#### 自定義類型序列化+ 反序列化 | ||
#### 自定義對象類型編碼 | ||
字段類型 | 字段長度(位元組) | 底層編碼格式 |
:---: | :----: | :----: |
自定義類 | 可變長 | Type(1) +Value(D1+D2+D3+...) |
Serializable 接口類
class Serializable
{
public:
virtual void serializable (DataStream & stream) const =0;
virtual bool unserializable (DataStream & stream) =0;
}
SERIALIZE宏(參數化實作)
#define SERIALIZE(...) \
void serialize(DataStream & stream) const \
{ \
char type = DataStream::CUSTOM; \
stream.write((char *)&type, sizeof(char)); \
stream.write_args(__VA_ARGS__); \
} \
\
bool unserialize(DataStream & stream) \
{ \
char type; \
stream.read(&type, sizeof(char)); \
if (type != DataStream::CUSTOM) \
{ \
return false; \
} \
stream.read_args(__VA_ARGS__); \
return true; \
}
大端與小端
位元組序列
位元組順序又稱為端序或尾序(Endianness),在計算機科學領域,指的是電腦記憶體中在數字通信鍊路中,組成多位元組的字的位元組排列順序。
小端
little-Endian:将低序位元組存儲在起始位址(在低位編位址),在變量指針轉換過程中位址儲存不變,比如,int64 轉到 int32,對于機器計算來說更友好和自然
大端
Big-Endian:将高序位元組存儲在起始位址(高位編制),記憶體順序和數字的書寫順序是一緻的,對于人的直覺思維比較容易了解,網絡位元組序統一采用Big-Endian
檢測位元組序
- 使用庫函數
#include <endian.h>
__BYTE_ORDER == __LITTLE_ENDIAN
__BYTE_ORDER == __BIG_ENDIAN
- 通過位元組存儲位址判斷
#include <stdio.h>
#include<string.h>
int main()
{
int n = 0x12345678;
char str[4];
memcpy(str,&n,sizeof(int));
for(int i = 0;i<sizeof(int);i++)
{
printf("%x\n",str[i]);
}
if(str[0]==0x12)
{
printf("BIG");
}else if (str[0] == 0x78){
printf("Litte");
}else{
printf("unknow");
}
}