NO.2|proto3语法|消息类型|通讯录|文件读取|enum类型

  • 不再打印联系⼈的序列化结果,⽽是将通讯录序列化后并写⼊⽂件中。
  • 从⽂件中将通讯录解析出来,并进⾏打印。
  • 新增联系⼈属性,共包括:姓名、年龄、电话信息、地址、其他联系⽅式、备注

字段规则

消息的字段可以⽤下⾯⼏种规则来修饰:

  • singular:消息中可以包含该字段零次或⼀次(不超过⼀次)。proto3语法中,字段默认使⽤该规则。
  • repeated:消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理解为定义了⼀个数组。

更新contacts.proto, PeopleInfo 消息中新增 phone_numbers 字段,表⽰⼀个联系⼈有多个号码,可将其设置为repeated,写法如下:

c 复制代码
syntax = "proto3";  
package contacts;  

message PeopleInfo {  
	string name = 1;  
	int32 age = 2;  
	repeated string phone_numbers = 3;  
}

消息类型的定义与使⽤

定义

在单个.proto⽂件中可以定义多个消息体,且⽀持定义嵌套类型的消息(任意多层)。每个消息体中的字段编号可以重复。

更新contacts.proto,我们可以将phone_number提取出来,单独成为⼀个消息:

c 复制代码
syntax = "proto3";  
package contacts;  

message PeopleInfo {  
	string name = 1;  
	int32 age = 2;  
	message Phone {  
		string number = 1;  
	}  
}
c 复制代码
syntax = "proto3";  
package contacts;  

message Phone {  
	string number = 1;  
}
message PeopleInfo {  
	string name = 1;  
	int32 age = 2;  
}
使⽤
  • 消息类型可作为字段类型使⽤

    contacts.proto

  • 可导⼊其他.proto⽂件的消息并使⽤

    Phone消息定义在phone.proto⽂件中

contacts.proto中的 PeopleInfo 使⽤ Phone 消息

在proto3⽂件中可以导⼊proto2消息类型并使⽤它们,反之亦然

创建通讯录2.0版本

通讯录2.x的需求是向⽂件中写⼊通讯录列表,以上我们只是定义了⼀个联系⼈的消息,并不能存放通讯录列表,所以还需要在完善⼀下contacts.proto(终版通讯录2.0):

进⾏⼀次编译

c 复制代码
protoc --cpp_out=. contacts.proto

编译后⽣成的 contacts.pb.h contacts.pb.cc 会将之前的⽣成⽂件覆盖掉

contacts.pb.h更新的部分代码

  • 每个字段都有⼀个clear_⽅法,可以将字段重新设置回empty状态。
  • 每个字段都有设置和获取的⽅法,获取⽅法的⽅法名称与⼩写字段名称完全相同。但如果是消息类型的字段,其设置⽅法为mutable_⽅法,返回值为消息类型的指针,这类⽅法会为我们开辟好空间,可以直接对这块空间的内容进⾏修改。
  • 对于使⽤repeated修饰的字段,也就是数组类型,pb为我们提供了add_⽅法来新增⼀个值,并且提供了_size⽅法来判断数组存放元素的个数。
通讯录2.0的写⼊实现
c 复制代码
// GOOGLE_PROTOBUF_VERIFY_VERSION 宏: 验证没有意外链接到与编译的头⽂件不兼容的库版本。如果检测到版本不匹配,程序将中⽌。注意,每个 .pb.cc ⽂件在启动时都会⾃动调⽤此宏。在使⽤ C++ Protocol Buffer 库之前执⾏此宏是⼀种很好的做法,但不是绝对必要的。  
GOOGLE_PROTOBUF_VERIFY_VERSION;

// 在程序结束时调⽤ ShutdownProtobufLibrary(),为了删除 Protocol Buffer 库分配的所有全局对象。对于⼤多数程序来说这是不必要的,因为该过程⽆论如何都要退出,并且操作系统将负责回收其所有内存。但是,如果你使⽤了内存泄漏检查程序,该程序需要释放每个最后对象,或者你正在编写可以由单个进程多次加载和卸载的库,那么你可能希望强制使⽤ Protocol Buffers 来清理所有内容。  
google::protobuf::ShutdownProtobufLibrary();
  • 运⾏write

  • 查看⼆进制⽂件

hexdump工具
通讯录2.0的读取实现
decode

可以⽤ protoc -h 命令来查看ProtoBuf为我们提供的所有命令option。其中ProtoBuf提供⼀个命令选项 --decode ,表⽰从标准输⼊中读取给定类型的⼆进制消息,并将其以⽂本格式写⼊标准输出。消息类型必须在.proto⽂件或导⼊的⽂件中定义

enum类型

定义规则

语法⽀持我们定义枚举类型并使⽤。在.proto⽂件中枚举类型的书写规范为:

  • 枚举类型名称:
    使⽤驼峰命名法,⾸字⺟⼤写。例如: MyEnum
  • 常量值名称:
    全⼤写字⺟,多个字⺟之间⽤ _ 连接。例如: ENUM_CONST = 0;
    定义⼀个名为PhoneType的枚举类型
c 复制代码
enum PhoneType {  

	MP = 0;   // 移动电话 
	TEL = 1;  // 固定电话

}
  1. 0值常量必须存在,且要作为第⼀个元素。这是为了与proto2的语义兼容:第⼀个元素作为默认值,且值为0。
  2. 枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)。
  3. 枚举的常量值在32位整数的范围内。但因负值⽆效因⽽不建议使⽤(与编码规则有关)。
定义时注意

将两个'具有相同枚举值名称'的枚举类型放在单个.proto⽂件下测试时,编译后会报错:某某某常量已经被定义!所以这⾥要注意:

  • 同级(同层)的枚举类型,各个枚举类型中的常量不能重名。
  • 单个.proto⽂件下,最外层枚举类型和嵌套枚举类型,不算同级。
  • 多个.proto⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都未声明package,每个proto⽂件中的枚举类型都在最外层,算同级。
  • 多个.proto⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都声明了package,不算同级。
c 复制代码
// ---------------------- 情况1:同级枚举类型包含相同枚举值名称---------------
enum PhoneType {  
	MP = 0;    // 移动电话 
	TEL = 1;   // 固定电话
}  

enum PhoneTypeCopy {  
	MP = 0; // 移动电话 
	// 编译后报错:MP 已经定义  
}  

// ---------------------- 情况2:不同级枚举类型包含相同枚举值名称------------- 
enum PhoneTypeCopy {  
	MP = 0; // 移动电话 
	// ⽤法正确  
}  

message Phone {  
	string number = 1;  // 电话号码
	enum PhoneType {
		MP = 0;  // 移动电话
		TEL = 1; // 固定电话 
	} 
}  

// ---------------------- 情况3:多⽂件下都未声明package-------------------- 
// phone1.proto  
import "phone1.proto"  
enum PhoneType {  
	MP = 0;   // 移动电话 
	TEL = 1;  // 固定电话
	// 编译后报错:MP 已经定义
}  

// phone2.proto  
enum PhoneTypeCopy {  
	MP = 0; // 移动电话  
}

// ---------------------- 情况4:多⽂件下都声明了package--------------------  
// phone1.proto  
import "phone1.proto"  
package phone1;  

enum PhoneType {  
	MP = 0;   // 移动电话
	TEL = 1;  // 固定电话
	// ⽤法正确
}  

// phone2.proto  
package phone2;  
enum PhoneTypeCopy {  
	MP = 0; // 移动电话  
}

同级枚举类型包含相同枚举值名称

多⽂件下都未声明package

多⽂件下都声明了package

升级通讯录⾄2.1版本

更新contacts.proto(通讯录2.1),新增枚举字段并使⽤

  • 编译
c 复制代码
protoc --cpp_out=.contacts.proto

contacts.pb.h更新的部分代码

  • 对于在.proto⽂件中定义的枚举类型,编译⽣成的代码中会含有与之对应的枚举类型、校验枚举值是否有效的⽅法_IsValid、以及获取枚举值名称的⽅法_Name。

  • 对于使⽤了枚举类型的字段,包含设置和获取字段的⽅法,已经清空字段的⽅法clear_。

  • 更新write.cc(通讯录2.1)

  • 更新read.cc(通讯录2.1)

  • 编译后进⾏读写验证

相关推荐
二哈赛车手7 小时前
新人笔记---ApiFox的一些常见使用出错
java·笔记·spring
代码搬运媛7 小时前
Jest 测试框架详解与实现指南
前端
栗子~~8 小时前
JAVA - 二层缓存设计(本地缓冲+redis缓冲+广播所有本地缓冲失效) demo
java·redis·缓存
YDS8298 小时前
DeepSeek RAG&MCP + Agent智能体项目 —— RAG知识库的搭建和接口实现
java·ai·springboot·agent·rag·deepseek
counterxing8 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq8 小时前
windows下nginx的安装
linux·服务器·前端
之歆9 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜9 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
Maimai108089 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
未若君雅裁9 小时前
MyBatis 一级缓存、二级缓存与清理机制
java·缓存·mybatis