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)

  • 编译后进⾏读写验证

相关推荐
小J听不清1 小时前
CSS 边框(border)全解析:样式 / 宽度 / 颜色 / 方向取值
前端·javascript·css·html·css3
用户255778850811 小时前
axios全局重复请求取消
前端
前端付豪1 小时前
实现一个用户可以有多个会话
前端·后端·llm
庞轩px1 小时前
MinorGC的完整流程与复制算法深度解析
java·jvm·算法·性能优化
zhouping@1 小时前
JAVA学习笔记day06
java·笔记·学习
林古1 小时前
我在 WSL 里控制 Windows Chrome 的一次实战复盘(OpenClaw)
前端
MinterFusion2 小时前
如何在开放麒麟(openKylin)下安装FTP服务器(v0.1.0)
运维·服务器·网络·vsftpd·开放麒麟·明德融创·openkylin
毕设源码-郭学长2 小时前
【开题答辩全过程】以 某某协会管理与展示平台为例,包含答辩的问题和答案
java
想不到一个好的ID2 小时前
Claude Code 初学者必看指南
前端·后端