个人主页:chian-ocean
文章专栏-Linux
序列化与反序列化技术:开发者必知的概念与应用
-
- 个人主页:chian-ocean
- 文章专栏-Linux
- 前言
- (反)序列化存在的必要性
-
- **什么是序列化和反序列化?(是什么)**
- **为什么要进行序列化和反序列化(为什么)**
- **序列化和反序列化的应用场景(干什么)**
- 序列化和反序列化的底层是什么(原理)?
- 手搓序列化和反序列化
-
- 序列化
-
- 请求序列化
-
- 详细解释
- 序列化示例
- 响应序列化
-
- **详细解释**
- **序列化后的示例**
- 反序列化
-
- 请求反序列化
-
- **详细解释**
- 反序列化示例
- 响应反列化
-
- 详细解释
- 反序列化示例
前言
序列化 和 反序列化 是计算机网络编程中的常见操作,尤其在客户端和服务器之间传输数据时。序列化将复杂的数据结构转化为字节流或可传输格式,而反序列化则是将传输的字节流或数据恢复成原始数据结构。通过这些操作,数据可以在不同的计算机、进程或系统之间进行高效交换。
(反)序列化存在的必要性
什么是序列化和反序列化?(是什么)
- 序列化:把内存中的数据(比如对象、数组、结构体等)转换为可以传输或存储的格式。通常是将数据转换为一串字节流(也可以是字符串,二进制等格式)。
- 反序列化:将字节流或字符串转换回原始的数据结构,方便程序继续使用。
为什么要进行序列化和反序列化(为什么)
- 将复杂数据结构转化为可传输的格式,以便能够通过网络或存储介质传输。
- 确保跨平台和跨语言的兼容性,使得不同系统之间能够无缝交换数据。
- 提高传输效率,尤其是在带宽受限的网络环境中,序列化格式可以通过压缩和优化数据大小来提升性能。
- 支持数据持久化,将数据保存到文件或数据库中,方便之后的恢复和使用。
- 支持标准化协议,确保各个系统之间按照统一协议交换数据。
- 确保数据一致性,避免因格式不一致导致的错误。
说白了就是为了网络传输,和数据交换兼容性问题。
序列化和反序列化的应用场景(干什么)
- 网络传输:在客户端和服务器之间交换数据时,数据需要转换成一种可以通过网络传输的格式,比如 JSON、XML 或二进制格式。
- 数据存储:把程序中的数据保存到文件或数据库中(持久化),以便以后恢复使用。
- 跨语言或跨平台的数据交换:不同操作系统和编程语言的数据表示方式可能不同,序列化和反序列化可以确保不同系统之间的数据传输是兼容的。
序列化和反序列化的底层是什么(原理)?
从内存流转向字节流的一个过程。如下,传入一个结构体,在不同的编译器下会有不同的结构体对齐情况。因此:
- 在传输的时候转向字符串的过程可以称之为**序列化(Serialize)**的过程
- 当我另外的进程(主机等等)通过解码恢复原来的结构体的过程可以称之为**反序列化(Deserialize)**的过程
序列化过程:
反序列化过程:
手搓序列化和反序列化
以下下的场景基于网络收发数据的过程。(客户端过网络请求服务端通两个数进行运算发送,服务端通过计算通过网络传输给客户端)
- 我们手到请求(Request)数据序列化并且添加数据报文。
class Requset
{
public:
Requset(int data1, char op, int data2)
: x_(data1), op_(op), y_(data2)
{
}
Requset()
{}
private:
int x_;
char op_;
int y_;
};
- 服务端响应计算(Calculate)的到结果传给客户端。
class Response
{
public:
Response(int ret, int code)
: retsult_(ret), code_(code)
{}
Response()
{}
~Response()
{}
private:
int retsult_;
int code_;
};
序列化
请求序列化
// 序列化:x + y
bool Serialize(std::string *out)
{
// 构建有效载荷
// 将整数 x_ 转换为字符串,并赋值给 s
std::string s = std::to_string(x_);
// 在字符串 s 后添加空格分隔符
s += space_sep;
// 添加运算符(例如:+,-,*,/)到字符串 s
s += op_;
// 再次添加空格分隔符
s += space_sep;
// 将整数 y_ 转换为字符串,并添加到字符串 s 后面
s += std::to_string(y_);
// 将构建好的字符串 s 赋值给输出参数 out
*out = s;
// 返回 true,表示序列化成功
return true;
}
详细解释
序列化目标:
- 该方法的目的是将当前类对象(包括 x_、op_ 和 y_)的数据序列化成一个字符串,使得数据可以方便地传输、存储或者进行后续处理。
- x_ 和 y_ 是整数类型的成员变量,op_ 是运算符(如 +、- 等)。
字符串构建:
- std::to_string(x_):将整数 x_ 转换为字符串形式。
- space_sep:空格字符,用作运算数和运算符之间的分隔符。此变量未在方法内部声明,假设它是一个类成员变量(例如 static const std::string space_sep = " ";),用于控制序列化中使用的分隔符。
- op_:将运算符 op_(例如 +、- 等)追加到字符串中。
- std::to_string(y_):将整数 y_ 转换为字符串形式。
构建最终的序列化字符串:
- 通过连续的字符串拼接,构建出形如 x op y 的字符串(例如,3 + 5)。
- 在运算符与操作数之间添加空格分隔符,以确保反序列化时能正确解析每个部分。
输出结果:
- 最终的字符串(如 "3 + 5")被赋值给 out 指向的 std::string。
- 由于参数 out 是一个指针,因此方法会直接修改传入的 std::string,返回最终结果。
返回值:
- true:表示序列化成功。
序列化示例
序列化: Serialize 函数将 x_ 和 y_ 转换为字符串,并在它们之间添加运算符 op_,然后通过空格分隔符拼接这些字符串,最后将结果赋值给输出参数 out。x_ = 5 y_= 3 字符串out = 5 + 3;
响应序列化
// 序列化 result code
bool Serialize(std::string *out)
{
// 构建有效载荷
// 将整数 retsult_ 转换为字符串,并赋值给 s
std::string s = std::to_string(retsult_);
// 在字符串 s 后添加空格分隔符
s += space_sep;
// 将整数 code_ 转换为字符串,并添加到字符串 s 后面
s += std::to_string(code_);
// 将构建好的字符串 s 赋值给输出参数 out
*out = s;
// 返回 true,表示序列化成功
return true;
}
详细解释
- 该方法的目的是将类的两个成员变量 retsult_ 和 code_ 序列化为一个字符串,以便进行传输、存储或其他后续处理。
- retsult_ 和 code_ 都是整数类型的成员变量,表示一些需要序列化的数据。
- std::to_string(retsult_):将整数 retsult_ 转换为字符串形式。
- space_sep:这是一个空格字符,作为分隔符,将两个整数的字符串形式分开。假设 space_sep 是一个类成员,定义了空格字符 static const std::string space_sep = " ";。
- std::to_string(code_):将整数 code_ 转换为字符串形式。
- 通过字符串拼接,构建形如 "retsult_ code_" 的字符串(例如,"123 456")。
- 在两个整数之间加入空格,确保反序列化时能够正确解析每个部分。
- 将构建的字符串 s 赋值给输出参数 out,这是一个指向 std::string 的指针。
- 由于 out 是指针,传入的字符串将被修改为序列化后的结果。
- true:表示序列化操作成功。
序列化后的示例
假设 retsult_ = 123 和 code_ = 456,调用 Serialize 方法后:
- 传入参数 out(一个 std::string*)将会包含序列化后的字符串 "123 456"。
反序列化
请求反序列化
// 反序列化 x + y
bool Deserialize(const std::string &in)
{
// 解析有效载荷
// 查找第一个空格的位置(分隔符)
auto pos1 = in.find(space_sep);
if (pos1 == std::string::npos) // 如果没有找到空格,返回 false,表示无效的输入
return false;
// 提取第一个操作数(x)部分,subtring(0, pos1) 提取从 0 到第一个空格前的字符串
std::string part_x = in.substr(0, pos1);
// 查找最后一个空格的位置(分隔符),用于区分运算符与第二个操作数
auto pos2 = in.rfind(space_sep);
// 提取运算符部分(即空格之间的部分),subtring(pos1 + 1, pos2) 提取第一个空格后,第二个空格前的字符串
std::string oper = in.substr(pos1 + 1, pos2);
// 提取第二个操作数(y)部分,subtring(pos2 + 1) 提取最后一个空格之后的部分
std::string part_y = in.substr(pos2 + 1);
// 确保第二个空格的位置与第一个空格之间的距离是 2,表示运算符长度为 1(只包含一个字符)
if (pos2 != pos1 + 2)
return false;
// 设置操作符 op_,存储在 pos1 + 1 处,表示操作符的位置
op_ = in[pos1 + 1];
// 将 part_x 和 part_y 转换为整数,存储到 x_ 和 y_ 中
x_ = std::stoi(part_x);
y_ = std::stoi(part_y);
// 返回 true,表示反序列化成功
return true;
}
详细解释
- 该方法的输入参数 in 是一个包含数学表达式的字符串,格式为 "x op y"(例如 "3 + 5")。
- space_sep 是一个字符串,通常为 " "(空格),用作分隔符,在序列化和反序列化过程中分隔操作数和运算符。
- auto pos1 = in.find(space_sep);:查找字符串中第一个空格的位置。这个位置标志着左操作数(x_)和运算符之间的分隔点。
- 如果没有找到空格,返回 false,表示输入格式不正确。
- std::string part_x = in.substr(0, pos1);:提取从字符串开头到第一个空格之前的部分,即左操作数(x_)。
- part_x 是一个字符串,后续会将其转为整数。
- auto pos2 = in.rfind(space_sep);:查找字符串中最后一个空格的位置。这个位置通常标志着运算符(op_)和右操作数(y_)之间的分隔点。
- std::string oper = in.substr(pos1 + 1, pos2);:提取位于两个空格之间的部分,即运算符。这里假设运算符是一个单字符(如 +、-、* 等)。
- oper 变量存储运算符,但实际上此变量在后续没有直接使用,可以省略。
- std::string part_y = in.substr(pos2 + 1);:提取第二个空格之后的部分,即右操作数(y_)。
- if (pos2 != pos1 + 2):检查第二个空格的位置,确保两个空格之间的间距为 2(即一个字符的运算符)。如果运算符不是单个字符(比如没有运算符或有多个字符),返回 false。
- op_ = in[pos1 + 1];:提取运算符,位置在第一个空格后面,存储在 op_ 变量中。
- x_ = std::stoi(part_x); 和 y_ = std::stoi(part_y);:将提取到的字符串 part_x 和 part_y 转换为整数,存储在 x_ 和 y_ 中。
反序列化示例
Deserialize 方法的目的是将一个字符串(例如 "3 + 5")反序列化为类的三个成员变量:x_(左操作数)、op_(运算符)和 y_(右操作数)
响应反列化
bool Deserialize(const std::string &in)
{
auto pos = in.find(space_sep);
std::string res = in.substr(0, pos);
std::string code = in.substr(pos + 1);
if (pos != in.rfind(space_sep))
return false;
retsult_ = std::stoi(res);
code_ = std::stoi(code);
return true;
}
详细解释
查找分隔符:
- auto pos = in.find(space_sep); 这行代码在输入字符串 in 中查找分隔符 space_sep 的第一个出现位置,并返回这个位置的索引值。
提取字符串的第一部分:
- std::string res = in.substr(0, pos); 这行代码提取从字符串 in 开始到分隔符位置之前的子串(不包括分隔符本身),这个子串用来存储第一个整数。
提取字符串的第二部分:
- std::string code = in.substr(pos + 1); 这行代码从分隔符的下一个位置开始,到字符串的末尾,提取出第二个子串,用来存储第二个整数。
检查是否有多个分隔符:
- if (pos != in.rfind(space_sep)) 这行代码检查在字符串中是否有多个 space_sep 分隔符。rfind 返回 space_sep 在字符串中最后一次出现的位置。如果第一个分隔符位置 pos 和最后一个分隔符位置 in.rfind(space_sep) 不相等,说明字符串中有多个分隔符,因此函数会返回 false,表示字符串格式无效。
将子串转换为整数:
-
如果字符串中只有一个分隔符,接下来的两行代码将子串转换为整数:
-
retsult_ = std::stoi(res); 将字符串 res 转换为整数,并赋值给 retsult_。
-
code_ = std::stoi(code); 将字符串 code 转换为整数,并赋值给 code_。
-
反序列化示例
输入字符串:我们在 main 函数中使用了一个输入字符串 "123 456",它包含两个整数,分别由空格分隔。
反序列化:调用 obj.Deserialize(input),函数尝试从字符串中提取出这两个整数,并赋值给 retsult_ 和 code_。
评论前必须登录!
注册