Protocol Buffers(Protobuf) 官方文档--Protobuf语言指南

Protocol Buffers(Protobuf) 官方文档--Protobuf语言指南html

约定:为方便书写,ProtocolBuffers在下文中将已Protobuf代替。java

本指南将向您描述如何使用protobuf定义i结构化Protobuf数据,包括.proto文件语法和如何使用.proto文件生成数据存取类。python

做为一个参考指南,本文档将以示例的形式一步步向您介绍Protobuf的特色。您能够参考您所选择的语言的示例。tutorial 编程

--------------------------------------小小的分割线-----------------------------------------数组

定义一个消息类型ide

首先,看一个很是简单的例子,好比说你想定义一个 搜索请求消息 ,每一个搜索请求都有一个 查询的字符串(关键字:好比咱们上百度搜索 《报告老板》),和咱们搜索出来的一个感兴趣的网页,以及搜索到的全部网页总数。 来看看这个.proto文件是如何定义的。ui

 

1 message SearchRequest {
2   required string query = 1;
3   optional int32 page_number = 2;
4   optional int32 result_per_page = 3;
5 }

这个"搜索请求"消息指定了三个字段(名称/属性 组合),每个你想要包含在这类型的信息内的东西,都必须有一个字段,每一个字段有一个名称和类型!google

指定字段类型

在上面的示例中,全部的字段都是标量类型(scalar types):两个整数(integers:page_number 和 result_per_page)和一个字符串(string:query:查询的关键字),不过你能够在你的字段内指定符合类型。包括枚举类型(enumerations)和其余的消息类型编码

分配指定标签号

如你所见,每一个消息的字段都有一个惟一的数字标签,这些标签用来表示你的字段在二进制消息(message binary format)中处的位置。而且一旦指定标签号,在使用过程当中是不能够更改的,标记这些标签号在1-15的范围内每一个字段须要使用1个字节用来编码这一个字节包括字段所在的位置和字段的类型!(须要更多关于编码的信息请点击Protocol Buffer Encoding)。标签号在16-2047须要使用2个字节来编码。因此你最好将1-15的标签号为频繁使用到的字段所保留。若是未来可能会添加一些频繁使用到的元素,记得留下一些1-15标签号。spa

最小可指定的标签号为1,最大的标签号为229 - 1或者536870911。不能使用19000-19999的标签号(FieldDescriptor::kFirstReservedNumber 至 FieldDescriptor::kLastReservedNumber) 这些标签号是为protobuf内部实现所保留的,若是你在.proto文件内使用了这些标签号Protobuf编译器将会报错!

 

指定字段规则

消息字段能够被指定为如下三种:

  • required: 完整的消息内必须拥有此字段。此字段是必须拥有的 (双方都要有)
  • optional: 完整的消息内此字段是可选的,可拥有也能够没有(双方可选)
  • repeated: 完整的消息内本字段的值能够拥有任意个,重复的值的次数会保存下来。(双方可选,数组)

由于历史的缘由:repeated字段若是是基本的数字类型的话会没法编码。新的代码应该使用特殊的关键字[packed=true] 来使其获得有效的编码.例如

[cpp]  view plain copy print ?
 
  1. repeated int32 samples = 4 [packed=true];  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到个人代码片
 
  1. repeated int32 samples = 4 [packed=true];  

注意:你应该当心将字段设置为required,若是你但愿在某些状况下取消required字段的读写,它将改变字段为optional属性,旧的的读取方将会认为此消息不彻底。可能会无心的将其丢弃。你应该考虑自定义一个消息检查程序。google的一些工程师认为使用optinal字段的好处大于required。可是显然这个观点并非通用的。

添加更多的消息类型

多个消息类型能够定义在同一个.proto文件内,这对定义多个有关联的消息是是十分有用的。例如,若是你想定义一个用于回复SearchResponse消息,你能够像这样在.proto内添加。

[cpp]  view plain copy print ?
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3;  
  5. }  
  6.   
  7. message SearchResponse {  
  8.  ...  
  9. }  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到个人代码片
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3;  
  5. }  
  6.   
  7. message SearchResponse {  
  8.  ...  
  9. }  

 

添加注释

添加注释的方式和C/C++是同样的。使用//

[cpp]  view plain copy print ?
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;// Which page number do we want?  
  4.   optional int32 result_per_page = 3;// Number of results to return per page.  
  5. }  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到个人代码片
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;// Which page number do we want?  
  4.   optional int32 result_per_page = 3;// Number of results to return per page.  
  5. }  

 

.proto文件会生成什么?

当你使用protobuf编译器编译一个.proto文件,它会生成在.proto内你描述的消息类型的操做代码,这些代码是根据你所选择的编程功能语言决定的。这些操做代码内包含了设置字段值 和读取字段值,以及序列化到输出流 和 从输入流反序列化。

C++:编译器会按照每一个.proto文件生成与其对应的.h和.cc文件,每一个消息相似都有独立的消息操做类。

Java:编译器将会生成一个.java文件和一个操做类,此操做类为全部消息类型所共有, 使用一个特别的Builder类为每一个消息类型实例化.

Python:有一点不一样 – 编译器会为每一个消息生成一个模块每一个模块有一个静态描述符, 该模块与一个元类在运行时建立一个须要的数据操做类。

从你所选择语言的例程,你能够找到更多关于API的内容, 须要关于API的详细信息, 参考: API reference.

标量值类型

一个消息的字段若是要使用标量可以使之为如下类型 –这个表格显示了在.proto文件内能够指定的类型, 与自动生成的相对类型!

.proto Type Notes C++ Type Java Type Python Type[2]
double   double double float
float   float float float
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. int32 int int
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. int64 long int/long[3]
uint32 Uses variable-length encoding. uint32 int[1] int/long[3]
uint64 Uses variable-length encoding. uint64 long[1] int/long[3]
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int int
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long int/long[3]
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 228. uint32 int[1] int
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 256. uint64 long[1] int/long[3]
sfixed32 Always four bytes. int32 int int
sfixed64 Always eight bytes. int64 long int/long[3]
bool   bool boolean boolean
string A string must always contain UTF-8 encoded or 7-bit ASCII text. string String str/unicode[4]
bytes May contain any arbitrary sequence of bytes. string ByteString str

 

你能够在Protocol Buffer Encoding.找到更多关于.这些类型如何编码,如何序列化定义消息的信息!

[1] 在Java中, 无符号32位和64位整数与其有符号相对应, 最高位用来保存符号!

[2] 在全部状况下, 设置某个字段的值将会执行类型检查确保其值是合法的!

[3] 64位或32位无符号整数在解码中会以long来解码, 给字段赋值的时候能够是int.可是在全部状况下,赋值的时候会转变为其目标类型 . 详见 [2].

[4] Python的字符串在解码时候会以unicode来描述,可是一样的能够给其赋值为ascii字符串  (此乃弦外之音).

 

Optional 字段与其默认值

如上所述,在描述一个消息的时候能够用optional指定字段约束,一个消息能够包含也能够不包含optional元素。当一个消息被解析,若是其没有一个optional字段,被解析的消息对象就会将其相对的字段设置为其字段的默认值。这个默认值能够在描述消息的时候被指定。例如。好比你想设置SearchRequest的 result_per_page的默认值为10.

[cpp]  view plain copy print ?
 
  1. optional int32 result_per_page = 3 [default = 10];  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到个人代码片
 
  1. optional int32 result_per_page = 3 [default = 10];  

 若是一个optional字段没有被指定其默认值。其默认值被自动替换为:

1.字符串:为空字符串.

2.bool:为false.

3.数字类型:为0;

4.枚举值:为第一个枚举值

枚举值

当你定义消息格式的时候, 也许你但愿其中的一个字段的的值为一个预约义的值类表中的一个. 比方说, 在SearchRequest消息中你想定义一个 corpus 字段, corpus字段的值能够为:" UNIVERSALWEBIMAGESLOCALNEWSPRODUCTS 或者 VIDEO". 你能够很是简单的给你的消息添加一个枚举类型 - 一个枚举字段类型其值指定被指定为一个常量的集合 (若是你尝试赋值一个不同的值, 解析器将会认为这个字段为未知字段). 在下面的例子中 咱们给corpus字段指定为枚举类型与其可能的值 :

[cpp]  view plain copy print ?
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3 [default = 10];  
  5.   enum Corpus {  
  6.     UNIVERSAL = 0;  
  7.     WEB = 1;  
  8.     IMAGES = 2;  
  9.     LOCAL = 3;  
  10.     NEWS = 4;  
  11.     PRODUCTS = 5;  
  12.     VIDEO = 6;  
  13.   }  
  14.   optional Corpus corpus = 4 [default = UNIVERSAL];  
  15. }  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到个人代码片
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3 [default = 10];  
  5.   enum Corpus {  
  6.     UNIVERSAL = 0;  
  7.     WEB = 1;  
  8.     IMAGES = 2;  
  9.     LOCAL = 3;  
  10.     NEWS = 4;  
  11.     PRODUCTS = 5;  
  12.     VIDEO = 6;  
  13.   }  
  14.   optional Corpus corpus = 4 [default = UNIVERSAL];  
  15. }  

你能够为一个枚举常量定义别名,若是你须要这样作的话须要将allow_alias设置为true。不然若是出现别名的话编译器将会报错! 

[cpp]  view plain copy print ?
 
  1. enum EnumAllowingAlias {  
  2.   option allow_alias = true;  
  3.   UNKNOWN = 0;  
  4.   STARTED = 1;  
  5.   RUNNING = 1;  
  6. }  
  7. enum EnumNotAllowingAlias {  
  8.   UNKNOWN = 0;  
  9.   STARTED = 1;  
  10.   // RUNNING = 1;  //不注释这行的话会引起一个错误异常  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到个人代码片
 
  1. enum EnumAllowingAlias {  
  2.   option allow_alias = true;  
  3.   UNKNOWN = 0;  
  4.   STARTED = 1;  
  5.   RUNNING = 1;  
  6. }  
  7. enum EnumNotAllowingAlias {  
  8.   UNKNOWN = 0;  
  9.   STARTED = 1;  
  10.   // RUNNING = 1;  //不注释这行的话会引起一个错误异常  

枚举值的范围必须在32位整数以内.枚举值的编码使用可变长度的整数,负数会很是低效因此,不推荐使用。你能够在一个消息内部定义一个枚举类型,好比上面的例子。或者也能够在消息的外部定义。这些枚举类型是能够在.proto文件内中重用的,你能够在消息内定义个枚举类型。而后在不一样的消息类型中使用它!可使用 MessageType.EnumType来访问。当你运行编译器编译.proto文件中的枚举类型时,生成的代码会有一个相对应的枚举值(JAVA 或者C++),或者有一个特别的EnumDescriptor类(python)用于在运行时生成一个符号常量集合。

更多关于枚举类型的信息查询 generated code guide 选择你使用的语言。

 待续....................

相关文章
相关标签/搜索