当前位置: 首页 > article >正文

protobuf序列化

protobuf

protobuf是一种开源跨平台的序列化数据结构的协议。其对于存储资料或在网络上进行通信的程序是很有用的

处理PROTOBUF_NAMESPACE_OPEN错误

#define PROTOBUF_NAMESPACE_OPEN \
  namespace google              \
  {                             \
    namespace protobuf          \
    {
#define PROTOBUF_NAMESPACE_CLOSE \
  }                              \
  }
#define PROTOBUF_NAMESPACE_ID google::protobuf
#define PROTOBUF_CONSTEXPR
#define PROTOBUF_ATTRIBUTE_REINITIALIZES
#define PROTOBUF_NODISCARD [[nodiscard]]
#define PROTOBUF_ALWAYS_INLINE
using namespace google;

处理无法编译

在这里插入图片描述

序列化

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程,与之相对应的过程称之为反序列化(Unserialization)。序列化和反序列化主要用于解决在跨平台和跨语言的情况下, 模块之间的交互和调用,但其本质是为了解决数据传输问题
实现数据序列化:

  • 要有原始数据
    • 复合类型 -> 最常见的情况
    • 基础数据类型
  • 通过某些方式 -> 另外一种形式的数据
  • 得到的数据干啥? -> 目的: 进行分发, 分发到不同的终端/平台, 保证不同的平台能正确解析
    • 网络传输—>内存到内存
    • 磁盘拷贝—>u盘拷贝—>内存到磁盘再到内存
      序列化目的不是为了加密, 为的是数据的跨平台传输
      序列化的整体过程:
  • 发送端
    • 原始数据 -> 序列化 (编码) -> 特殊格式的字符串
    • 发送这个字符串
  • 接收端:
    • 接收数据
    • 特殊格式的字符串 -> 反序列化 (解码) -> 原始数据
    • 对原始数据进行处理

网络通信中的问题分析

  • 发送过程中遇到的一些问题?
  • 平台不同
    • 32bit / 64bit
      • long
    • 平台不同, 某些数据类型占用的内存大小不同
  • 如果不是字符串, 需要进行字节序转换
    • 字符串没有字节序问题, 字符在内存中只占一个字节
    • 如果发送的是结构体
      struct Test
      {
          int number;
          char buf[12];
          long sex;
      };
      
      Test t;
      send()/write()
      send(fd, (void*)t, sizeof(t), 0);
      
    • 大小端问题
  • 语言不同
  • 语言不同数据类型占用的内存有可能不同
    • c -> char -> 1字节
    • java -> char -> 2字节
  • 字节对齐问题

常用的序列化方式

  1. XML( Extensible Markup Language )类似于html

    XML是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。XML历史悠久,其1.0版本早在1998年就形成标准,并被广泛使用至今。

    XML的最初产生目标是对互联网文档进行标记,所以它的设计理念中就包含了对于人和机器都具备可读性。 但是,当这种标记文档的设计被用来序列化对象的时候,就显得冗长而复杂。

    XML基本格式:

    <?xml version="1.0" encoding="utf-8"?>
    <Library>
        <Type name="小说">
            <Book author="J.K.ROWLING" price="12$">哈利波特1</Book>
            <Book author="J.K.ROWLING" price="12$">哈利波特2</Book>
            <Book author="J.K.ROWLING" price="12$">哈利波特3</Book>
            <Book author="J.K.ROWLING" price="12$">哈利波特4</Book>
        </Type>
        <Type name="历史">
            <Book author="司马迁" price="20$">史记</Book>
        </Type>
    </Library>
    
  2. Json( JavaScript Object Notation )

    JSON起源于弱类型语言Javascript,它的产生来自于一种称之为"关联数组(Associative array)"的概念,其本质是就是采用"键值对"的方式来描述对象。

    JSON格式保持了XML的人眼可读的优点,非常符合工程师对对象的理解。

    相对于XML而言,序列化后的数据更加简洁(XML所产生序列化之后文件的大小接近JSON的两倍),而且其协议比较简单,解析速度比较快。

    JSON格式具备Javascript的先天性支持,所以被广泛应用于Web browser的应用常景中,是Ajax的事实标准协议。

    更多资料可查看:http://json.org/

    // json是一种数据格式, 不是语言, 和平台语言无关
    // json数组
    [整形, 浮点型, 布尔类型, 字符串, json数组, json对象]
    [12, 12.44, true, "hello", [1,2,3]]
    // json对象
    {
        "key":"value"
    }
    json对象中是n个键值对
    key: 必须是字符串
    value: 
    	整形
    	浮点型
    	布尔
    	字符串
    	json数组
    	json对象
    
    注意事项:
    	在一个文件中只能存储一个大的数组或者对象, 但是可以嵌套使用
    	原素和原始之间使用逗号间隔(一个键值对视为一个元素)
    	最后一个元素后边没有逗号
    
    {
        "lilei":"112334",
        "tom":"helolll",
        "lucy":"xxxxyyyy"
    }
    
    ["张三", "历史"]
    
    {
        "张三":{
            "father":"张三丰",
            "mother":"xxxx",
            "sister""xxx",
            "favorite":["足球", "乒乓", "游泳"]
        }
    	"李四":{
        }
    }
    
  3. Protocol Buffer

  4. ASN.1 抽象语法标记(Abstract Syntax Notation One)

  5. boost 序列化的类

编写proto文件并且生成对应的类

  • 操作流程:
  • 准备数据
    • 复合类型: 结构体/ 类
    • 基础类型
  • 创建一个新文件 xxx.proto
  • 将我们要序列化的数据 -> 写入到proto文件
    • 有语法格式
  • 通过一个命令 protocxxx.proto文件生成一个c++的类
    • 对应一个头文件/ 源文件
    • 操作命令-> 在window终端中: protoc xxx.proto --cpp_out=./
  • 直接使用这个类
    • 里边有对数据操作的api
      • 读数据 api
        • 方法名字 变量名()
      • 写数据 api
        • 方法名字: set_变量名(arg)

debug和release介绍

debug是在开发过程中使用的
release是在发行的时候使用的
在release模式下打断点不停

protobuf解析基本数据类型案例

#include <iostream>
#include "Persion.pb.h"
using namespace std;


/*
	 message Person
	{
		int64 id = 1;
		bytes name = 2;
		string sex = 3;
		int64 age = 4;
	}
*/
int main()
{
	//1.创建person对象,并且初始化
	Person p;
	p.set_id(1001);
	p.set_name("路飞");
	p.set_sex("man");
	p.set_age(18);
	//2.将person对象序列化  --  字符串
	string output;
	p.SerializeToString(&output);//返回值是bool 因此参数是传出参数
	cout << "序列化后的数据是:" << output << endl;
	//3.数据传输
	//一台电脑传入另外一台电脑 接收数据拿到的还是output
	//4.接收数据,解析(output) --解码
	//4.1创建对象
	Person p1;
	//4.2解析
	p1.ParseFromString(output);
	//5.处理原始数据  -- 打印数据信息
	cout << "id = " << p1.id() << endl;
	cout << "name = " << p1.name() << endl;
	cout << "sex = " << p1.sex() << endl;
	cout << "age = " << p1.age() << endl;

	system("pause");
	return 0;
}

在这里插入图片描述

protobuf解析数组案例

  // 要求name有多个 -> 数组
  syntax = "proto3";
  message Persion
  {
      int32 id = 1;   // 编号从1开始
      // vector<string> name;
      repeated bytes name = 2;	// name可以在程序中创建多个, 在程序中作为动态数组来使用
      string sex = 3;
      int32 age = 4;
  }

生成的头文件:

  void set_name(int index, const std::string& value);
  void set_name(int index, std::string&& value);
  void set_name(int index, const char* value);
  void set_name(int index, const void* value, size_t size);
  std::string* add_name();
  void add_name(const std::string& value);
  void add_name(std::string&& value);
  void add_name(const char* value);
  void add_name(const void* value, size_t size);

在main函数里:
当调用 add_name() 时,虽然得到了一个指向新添加字符串的指针,但指针本身并不会 “往后移动”。其实,add_name() 每次调用后会返回一个指向新添加元素的指针,而不是修改原有指针的地址。每次调用 add_name() 时,都会向内部的 std::vectorstd::string(或类似的容器)添加一个新元素,然后返回该元素的地址。

#include <iostream>
#include "Persion.pb.h"
using namespace std;


/*
	 message Person
	{
		int64 id = 1;
		repeated bytes name = 2;
		string sex = 3;
		int64 age = 4;
	}
*/
int main()
{
	//1.创建person对象,并且初始化
	Person p;
	p.set_id(1001);
    p.add_name();//可以理解为创造一块新的内存
	p.set_name(0, "路飞");

	p.add_name();
	p.set_name(1, "艾斯");

	p.add_name();
	p.set_name(2, "萨博");
	p.set_sex("man");
	p.set_age(17);
	p.set_sex("man");
	p.set_age(18);
	//2.将person对象序列化  --  字符串
	string output;
	p.SerializeToString(&output);
	cout << "序列化后的数据是:" << output << endl;
	//3.数据传输
	//
	//4.接收数据,解析(output) --解码
	//4.1创建对象
	Person p1;
	//4.2解析
	p1.ParseFromString(output);
	//5.处理原始数据  -- 打印数据信息
	cout << "id = " << p1.id() << endl;
	cout << "name = " << p1.name(0) << p1.name(1) << p1.name(2) << endl;
	cout << "sex = " << p1.sex() << endl;
	cout << "age = " << p1.age() << endl;

	system("pause");
	return 0;
}

在这里插入图片描述

protobuf中的枚举

syntax = "proto3";

enum Color//枚举也是类型
{
	Red = 0;//protobuf中第一个枚举值必须是0
	Green = 6;
	Blue = 9;
}
message Person
{
	int64 id = 1;
	repeated bytes name = 2;
	string sex = 3;
	int64 age = 4;
	Color color = 5;
}
#include <iostream>
#include "Persion.pb.h"
using namespace std;
int main()
{
	//1.创建person对象,并且初始化
	Person p;
	p.set_id(1001);
    p.add_name();
	p.set_name(0, "路飞");

	p.add_name();
	p.set_name(1, "艾斯");

	p.add_name();
	p.set_name(2, "萨博");
	p.set_sex("man");
	p.set_age(17);
	p.set_sex("man");
	p.set_age(18);
	// 枚举
	p.set_color(Blue);
	//2.将person对象序列化  --  字符串
	string output;
	p.SerializeToString(&output);
	cout << "序列化后的数据是:" << output << endl;
	//3.数据传输
	//
	//4.接收数据,解析(output) --解码
	//4.1创建对象
	Person p1;
	//4.2解析
	p1.ParseFromString(output);
	cout << "id: " << p1.id() << ", name: "
		<< p1.name(0) << ", "
		<< p1.name(1) << ", "
		<< p1.name(2)
		<< ", sex: " << p1.sex() << ", age: " << p1.age()
		<< ", color: " << p1.color()
		<< endl;

	system("pause");
	return 0;
}

在这里插入图片描述

在proto文件中导入另外一个proto

syntax = "proto3";
//导入另外一个proto文件
import "Info.proto";

enum Color
{
	Red = 0;//protobuf中第一个枚举值必须是0
	Green = 6;
	Blue = 9;
}
message Person
{
	int32 id = 1;
	repeated bytes name = 2;
	string sex = 3;
	int32 age = 4;
	Color color = 5;
	Info info = 6;
}
syntax = "proto3";

message Info
{
    bytes address = 1;  // 地址
    int32 number = 2;   // 门牌号
}
#include <iostream>
#include "Persion.pb.h"
using namespace std;
int main()
{
	//1.创建person对象,并且初始化
	Person p;
	p.set_id(1001);
    p.add_name();
	p.set_name(0, "路飞");

	p.add_name();
	p.set_name(1, "艾斯");

	p.add_name();
	p.set_name(2, "萨博");
	p.set_sex("man");
	p.set_age(17);
	p.set_sex("man");
	p.set_age(18);
	// 枚举
	p.set_color(Blue);
	Info * info = p.mutable_info();
	info->set_address("hello 新世界");
	info->set_number(101);
	//2.将person对象序列化  --  字符串
	string output;
	p.SerializeToString(&output);
	cout << "序列化后的数据是:" << output << endl;
	//3.数据传输
	//
	//4.接收数据,解析(output) --解码
	//4.1创建对象
	Person p1;
	//4.2解析
	p1.ParseFromString(output);
	//将Info对象的值取出来
	//当右边是一个 const itwz::Persion& 类型的返回值时,C++ 的编译器会将 const 引用视为一个有效的拷贝源。这意味着,即使返回的是 const 引用,C++ 仍会允许拷贝构造一个新的对象。
	Info li = p1.info();//info()的返回值是const &
	//const Info &li=p1.info();
	cout << "id: " << p1.id() << ", name: "
		<< p1.name(0) << ", "
		<< p1.name(1) << ", "
		<< p1.name(2)
		<< ", sex: " << p1.sex() << ", age: " << p1.age()
		<< ", color: " << p1.color()
		<< ", address: "<<li.address()
		<< ", number: "<<li.number()
		<< endl;

	system("pause");
	return 0;
}

在这里插入图片描述

proto文件同名,在protobuf中添加命名空间

包–>命名空间
两个都是message Persion

syntax = "proto3";
//导入另外一个proto文件
import "Info.proto";
//添加命名空间
package itcast;//Persion类中的命名空间
enum Color
{
	Red = 0;//protobuf中第一个枚举值必须是0
	Green = 6;
	Blue = 9;
}
message Persion
{
	int32 id = 1;
	repeated bytes name = 2;
	string sex = 3;
	int32 age = 4;
	Color color = 5;
	itwz.Persion info = 6;
}
syntax = "proto3";
//Persion类属于itwz这个命名空间
package itwz;
message Persion
{
    bytes address = 1;  // 地址
    int32 number = 2;   // 门牌号
}
#include <iostream>
#include "Persion.pb.h"
using namespace std;
using namespace itcast;

int main()
{
	//1.创建person对象,并且初始化
	Persion p;
	p.set_id(1001);
    p.add_name();
	p.set_name(0, "路飞");

	p.add_name();
	p.set_name(1, "艾斯");

	p.add_name();
	p.set_name(2, "萨博");
	p.set_sex("man");
	p.set_age(17);
	p.set_sex("man");
	p.set_age(18);
	// 枚举
	p.set_color(Blue);
	itwz::Persion* info = p.mutable_info();
	info->set_address("hello 新世界");
	info->set_number(101);
	//2.将person对象序列化  --  字符串
	string output;
	p.SerializeToString(&output);
	cout << "序列化后的数据是:" << output << endl;
	//3.数据传输
	//
	//4.接收数据,解析(output) --解码
	//4.1创建对象
	Persion p1;
	//4.2解析
	p1.ParseFromString(output);
	//将Info对象的值取出来
	itwz::Persion li = p1.info();
	cout << "id: " << p1.id() << ", name: "
		<< p1.name(0) << ", "
		<< p1.name(1) << ", "
		<< p1.name(2)
		<< ", sex: " << p1.sex() << ", age: " << p1.age()
		<< ", color: " << p1.color()
		<< ", address: "<<li.address()
		<< ", number: "<<li.number()
		<< endl;

	system("pause");
	return 0;
}

在这里插入图片描述


http://www.kler.cn/news/368073.html

相关文章:

  • 鸿蒙原生应用开发及部署:首选华为云,开启HarmonyOS NEXT App新纪元
  • 深入理解跳出率:如何利用百度统计优化网站用户体验
  • springboot083基于springboot的个人理财系统--论文pf(论文+源码)_kaic
  • unity项目导出安卓工程后,在AndroidStudio打包报错:unityLibrary:BuildIl2CppTask‘.
  • 【知识科普】正则表达式深入解读
  • 用“堆”模拟实现“优先级队列”
  • 解读AVL树:平衡二叉搜索树的奥秘
  • python 爬虫 入门 五、抓取图片、视频
  • 建造者设计模式
  • 基于知识图谱的苹果病虫害知识图谱问答
  • redis详细教程(2.List教程)
  • 如何快速开发一套基于Java的诊所管理系统?
  • C++设计模式——Factory Method工厂方法模式
  • C#文件内容检索的功能
  • P11232 [CSP-S 2024] 超速检测(民间数据)
  • ES6:let和const命令解读以及变量的解构赋值
  • PostgreSQL(十三)pgcrypto 扩展实现 AES、PGP 加密,并自定义存储过程
  • Flink CDC系列之:学习理解核心概念——Transform
  • Elasticsearch 解析:倒排索引机制/字段类型/语法/常见问题
  • 双击热备和负载均衡的区别
  • 头歌数据库实验 MySQL
  • Redis 哨兵 总结
  • Vue3学习:番茄钟案例的实现及打包遇到的几个问题
  • Python 自动化运维:Python基础知识
  • Vuejs设计与实现 — 渲染器核心:挂载与更新
  • 【C++单调栈 贡献法】907. 子数组的最小值之和|1975