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


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

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

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

    作为一个参考指南,本文档将以示例的形式一步步向您介绍Protobuf的特点。您可以参考您所选择的语言的示例。tutorial 

    --------------------------------------小小的分割线-----------------------------------------

    定义一个消息类型

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

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

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

    指定字段类型

    在上面的示例中,所有的字段都是标量类型(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标签号。

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

    指定字段规则

    消息字段可以被指定为以下三种:

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

    因为历史的原因:repeated字段如果是基本的数字类型的话会无法编码。新的代码应该使用特殊的关键字[packed=true] 来使其得到有效的编码.例如

    1. repeated int32 samples = 4 [packed=true];  
    1. repeated int32 samples = 4 [packed=true];  

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

    添加更多的消息类型

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

    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. }  
    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++是一样的。使用//

    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. }  
    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 TypeNotesC++ TypeJava TypePython 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.

    1. optional int32 result_per_page = 3 [default = 10];  
    1. optional int32 result_per_page = 3 [default = 10];  

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

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

    2.bool:为false.

    3.数字类型:为0;

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

    枚举值

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

    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. }  
    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。否则如果出现别名的话编译器将会报错! 

    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;  //不注释这行的话会引发一个错误异常  
    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 选择你使用的语言。

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

  • 相关阅读:
    WIN API下的窗口创建
    SharePoint阻止字段更新后引发相关事件
    hdu 3018 Ant Trip 求欧拉路径
    状态压缩dp与树形dp
    位运算符
    电影播放器mplayer设置常用选项
    poj 2249Binomial Showdown
    poj 3370 Halloween treats
    MPlayer配置文件
    MPlayerLinux 媒体播放器的安装与使用
  • 原文地址:https://www.cnblogs.com/yymn/p/4483370.html
Copyright © 2020-2023  润新知