目录
参考文档: 爱编程的大丙protobuf
学习视频: Protobuf教程
概述
protobuf中的数据类型 和 C++ 数据类型对照表:
普通类型序列化和反序列化
假设我们需要对一下结构进行序列化操作+
struct Person
{
int id;
string name;
string sex;
int age;
}
参考序列化结构,书写 proto文件
Person.proto 后缀必须是 .proto
注意消息体名称和结构体名称一样,C++类型和proto类型的不同,每个类型的编号也要不一样
执行命令:protoc ./Person.proto --cpp_out=./
生成 Person.pd.cc Person.pd.h
文件
实际上,当我们执行protoc命令之后,就将对应的消息体生成了一个类,类名就是我们消息体(message)的名字,之后通过调用这个类的各种方法(可以打开.pd.cc文件查看),实现数据的序列化的反序列化
#include <iostream>
#include "Person.pb.h"//不要忘记引入头文件
int main()
{
// 序列化
Person p;
p.set_id(10);
p.set_age(32);
p.set_sex("man");
p.set_name("lufey");
// 序列化操作
std::string output; // 存储成二进制
p.SerializeToString(&output);
// 反序列化
Person pp;
pp.ParseFromString(output);
std::cout << pp.id() << " " << pp.sex() << " " << pp.name() << " " << pp.age() << std::endl;
return 0;
}
嵌套类型的序列化和反序列化
1.proto文件如下
2.执行 protoc -I=./ Person.proto --cpp_out=./
命令生成 Person.pd.cc Person.pd.h
文件
注意:-I = proto文件路径
空格 proto文件名
3.运行
#include <iostream>
#include "Person.pb.h"//记住引入头文件
int main()
{
// 序列化
Person p;
p.set_id(10);
p.set_age(32);
p.set_sex("man");
p.set_name("lufey");
//嵌套类型设置参数
p.mutable_my_addr()->set_addr("湖北");
p.mutable_my_addr()->set_num(1001);
// 序列化操作
std::string output; // 存储成二进制
p.SerializeToString(&output);
// 反序列化
Person pp;
pp.ParseFromString(output);
std::cout << pp.id() << " " << pp.sex() << " " << pp.name() << " " << pp.age() << std::endl;
std::cout << pp.my_addr().addr() << " " << pp.my_addr().num() << std::endl;
return 0;
}
数组类型的序列化和反序列化
注意,数组类型需要使用repeated修饰
#include <iostream>
#include "Person.pb.h"
int main()
{
// 序列化
Person p;
p.add_name(); // 申请一块数组内存
p.set_name(0, "路飞"); // 设置数组的第一个元素(需要指定索引)
p.add_name("艾斯"); // 添加数组的第二个元素
p.add_name("萨博"); // 添加数组的第三个元素
// 序列化操作
std::string output; // 存储成二进制
p.SerializeToString(&output);
// 反序列化
Person pp;
pp.ParseFromString(output);
// 输出数组元素
int size = pp.name_size();
for (int i = 0; i < size; i++)
{
std::cout << pp.name(i) << std::endl;
}
return 0;
}
使用枚举
注意:protoc枚举类型第一个枚举值必须为0,其他的随意
#include <iostream>
#include "Person.pb.h"
int main()
{
// 序列化
Person p;
p.set_color(Color::Blue); // 设置枚举的值
// 序列化操作
std::string output; // 存储成二进制
p.SerializeToString(&output);
// 反序列化
Person pp;
pp.ParseFromString(output);
std::cout << "颜色:" << pp.color() << std::endl;//获取枚举的值
return 0;
}
proto文件中导入其他proto文件
1.编写proto文件
使用import导入
2.两个proto文件都需要执行protoc命令
分别会生成两个类
3.执行
#include <iostream>
#include "Person.pb.h"//两个头文件都要引入
#include "Address.pb.h"
int main()
{
// 序列化
Person p;
p.set_id(101);
p.set_age(15);
p.mutable_addr()->set_addr("湖北");
// 序列化操作
std::string output; // 存储成二进制
p.SerializeToString(&output);
// 反序列化
Person pp;
pp.ParseFromString(output);
std::cout << pp.id() << pp.age() << " " << pp.addr().addr() << std::endl; // 获取枚举的值
return 0;
}
proto的命名空间概念
使用 import 命名空间名
下面这个属于 Erbing这个命名空间
下面这个属于 Dabing 这个命名空间
代码需要带上命名空间(和C++一样)
#include <iostream>
#include "Person.pb.h"
#include "Address.pb.h"
//第一种写法
//using namespace Erbing;
//using namespace Dabing;
using namespace
int main()
{
// 序列化
Erbing::Person p;//第二种写法(带上命名空间)
p.set_id(101);
p.set_age(15);
p.mutable_addr()->set_addr("湖北");
// 序列化操作
std::string output; // 存储成二进制
p.SerializeToString(&output);
// 反序列化
Erbing::Person pp;
pp.ParseFromString(output);
std::cout << pp.id() << pp.age() << " " << pp.addr().addr() << std::endl; // 获取枚举的值
return 0;
}
序列化和反序列化
序列化:
// 头文件目录: google\protobuf\message_lite.h
// --- 将序列化的数据 数据保存到内存中
// 将类对象中的数据序列化为字符串, c++ 风格的字符串, 参数是一个传出参数
bool SerializeToString(std::string* output) const;
// 将类对象中的数据序列化为字符串, c 风格的字符串, 参数 data 是一个传出参数
bool SerializeToArray(void* data, int size) const;
// ------ 写磁盘文件, 只需要调用这个函数, 数据自动被写入到磁盘文件中
// -- 需要提供流对象/文件描述符关联一个磁盘文件
// 将数据序列化写入到磁盘文件中, c++ 风格
// ostream 子类 ofstream -> 写文件
bool SerializeToOstream(std::ostream* output) const;
// 将数据序列化写入到磁盘文件中, c 风格
bool SerializeToFileDescriptor(int file_descriptor) const;
反序列化:
// 头文件目录: google\protobuf\message_lite.h
bool ParseFromString(const std::string& data) ;
bool ParseFromArray(const void* data, int size);
// istream -> 子类 ifstream -> 读操作
// wo ri
// w->写 o: ofstream , r->读 i: ifstream
bool ParseFromIstream(std::istream* input);
bool ParseFromFileDescriptor(int file_descriptor);