Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准.html
我理解的就是:它是一种轻便高效的结构化数据存储格式,能够用于结构化数据串行化,或者说序列化。方便文件的存储与网络传输.ios
咱们本身就不用定义它们的存储与传输协议了.git
第一步, 写一个proto的文件 .定义你须要的数据结构.github
每二步, 使用你想要用的语言的proto文件编译器把写的proto文件编译为目标语言的相关类. (目前google提供了 C++、Java、Python 三种语言的 API).shell
第三步, 把第二步生成的类包含到你写的程序中, 就可使用它了.ubuntu
看个基于C++主语言的例子,下面是一个.proto文件,网络
package information // 定义了package的名字 message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; }
当咱们命名这个文件时,用一个良好的命名习惯: 以这个的格式,packagename.MessageName.proto,因此这里的话,咱们能够把这个文件命名为 information.Person.proto数据结构
而后,咱们把.proto文件用编译器编译成C++的代码之后生成了两个文件:information.Person.h和information.Person.cc文件.ui
如今咱们就能够用它了.google
如定义一个Person的类对象,而后设置它里面的值(经过类的方法),populate,
Person person; person.set_name("John Doe"); person.set_id(1234); person.set_email("jdoe@example.com"); fstream output("myfile", ios::out | ios::binary); person.SerializeToOstream(&output); //把它序列化到输入输出流中,这里就是写文件中了.而后呢,咱们能够从文件中再次retrieve出来 .
fstream input("myfile", ios::in | ios::binary); //读出文件 Person person; person.ParseFromIstream(&input); //反序列化它 cout << "Name: " << person.name() << endl; cout << "E-mail: " << person.email() << endl;
protobuf的安装:
方法一: 若是你用的ubuntu或debian的话,能够运行: sudo apt-get install protobuf-complier 命令直接安装.
方法二: 能够参考这里,https://github.com/google/protobuf,里面是什么我没有怎么仔细看哦,若是想简单安装的话,能够来这里下载protobuf-2.5.0tar.gz, 连接为:http://pan.baidu.com/s/1i5jsGiL,密码:50fo,下载完之后执行下面操做:
1,执行 tar –zxf protobuf-2.5.0.tar.gz 命令,解压文件;
2,执行 cd protobuf-2.5.0命令,进入目录。
3,执行 ./configure --prefix=加本身想安装到的绝对目录, 设置安装目录;
4,执行:make 进行编译
5, 执行: make check
6,执行: make install 进行安装;
最后呢, 加入它的环境变量,能够上系统找到它: export PATH= /home/work /protobuf/bin:$PATH
而后,你在shell里执行: protoc –version 命令,就会显示 libprotoc 2.5.0.
安装成功;
首先,咱们定义一个.proto文件,下面的消息SearchRequest定义了3个字段.并以此为例:
message SearchRequest { required string query = 1; optional int32 page_number = 2; optional int32 result_per_page = 3; }
字段的类型: 能够为标量形式,如 string, int32等 ,也能够为复合形式,如枚举类型或其它的message类型,常见的类型以下(能看清哈):
分配的标记: 如上面所示,它们后面都有一个数字,干什么用的呢?它们的做用就是在以二进制形式的时候,可能经过这个分配的数字标记标识对应的字段,数字的范围能够从1至2次方-1. 1-15占一个字节(包括这个数字与字段类型),16-2047占两个字节.另个19000-19999是保留的数字标记. 在使用标记时,尽量让常常出现的字段表示为1-15. 还有,咱们要小的标记为未来使用.
字段的rules:
required: 在一个well-formed mmessage里,必定 要在这么一个字段.
optional: 表示能够存在能够不存在的字段.
repeated: 表示能够重复的字段,重复的次数能够为0哦. 另外,这些重复的values的顺序也会被保留下来.因为历史缘由,为了让repeated的字段能够更好的encode,如今的新代码都会使一个选项[packed = true],如: repeated int32 samples = 4 [packed=true];
注意:当使用required字段的时候,要特征特别特别的当心哦,为何呢?当咱们在一个meassage里面定义了required字段的时候,若是咱们有时候不想写它或着发送requied字段的时候,这时候就会出现问题,old readers会认为这个message不完整,它们就会把拒绝或着丢掉它. 因此,google的不少工程师认为,required的good 小于 harm, 因此他们选择只用optional 和repeated.
咱们能够在一个.proto文件里面定义多个message.在.proto文件里面,咱们用 // 来增长注释.
保留字段:在咱们在删除或者注释了一个字段后,若是后来有别人使用了咱们的.proto文件,为了防止别人再用咱们删除或着注释过的字段的标记或名字时出现访问时的数据错误,咱们应该保留咱们使用过的字段的标记或名字.方法以下:
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; } //注意:咱们不能把标记与名字和放到一个 reserved里.
对于optional 字段, 咱们能够设置它的默认值.当optional字段没有一个value的时候,就用默认的value代替.格式好比:
optional int32 result_per_page = 3 [default = 10];//就就是默认10了.另外,若是咱们没有设置认字段的时候,它会根据类型,设置系统的默认value. 对于 string, 默认为空字符, 对于bool类型, 默认为 false , 对于 数字类型,默认为0, 对于枚举类型,默认列表里的第一个值(因此啊,设置枚举类型时,必定特别注意啦)
message SearchRequest { required string query = 1; optional int32 page_number = 2; optional int32 result_per_page = 3 [default = 10]; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } optional Corpus corpus = 4 [default = UNIVERSAL]; }
1. 咱们能够在枚举变量里面定义一个变量的别名,方法是咱们把不一样变量名的变量值定义成同样的,而且设置 变量 allow_alias 变true.如:
enum EnumAllowingAlias { option allow_alias = true; //必定变忘了,要不会出错的. UNKNOWN = 0; STARTED = 1; //下面的变量的值都为1 RUNNING = 1; }2. 枚举类型的值应该为一个32位的整数, 因为枚举类型的值在encoding时,为变长整数的编码方式,对于 负数来讲效率是很低的,因此不建议用负数做为值.
3. 咱们能够把枚举类型定义在一个message里面,也能够定义在外面,这样的话,在一个.proro文件里,全部的message均可以使用它. 另外,也能够把一个枚举类型定义在其它的.proto文件里,使用的时候的syntax为:
MessageType.EnumType
.
咱们可使用一个message类型做为一个字段的type.
message SearchResponse { repeated Result result = 1; } message Result { required string url = 1; optional string title = 2; repeated string snippets = 3; }
当咱们想使用别的 .proto文件里定义的message的时候怎么办呢? 咱们能够像在C/C++里导入头文件同样,使用import语句,能够把别的.proto文件导入进来. 如:
import "myproject/other_protos.proto";默认地,咱们只能使用直接导入的.proto文件,(即,若是咱们import的一个文件里又import了其它.proto文件,可是咱们不能使用间接使用哦). 不过有一个方法, 可让咱们作到传递式的引用.即, import pulic notion.
// 第一个 .proto文件的位置; //各类定义;// 第二个.proto文件 import public "第一个文件" import "其它文件"// 各类定义// 第三个.proto文件 import "第二个文件" //这是,咱们可使用第一个文件里的定义,第二个文件里的定义,可是不能使用其它文件里的定义import文件时的搜索路经: 能够经过 –I/ –proto_path指定搜索路经, 或着it looks in the directory in which the compiler was invoked.
(对于这部分如今不先看,由于用不着,要不看了也没有用.)
当咱们有不少可选的字段的时候,而且在不少状况下最多有一个字段被使用时,这时咱们能够做用Oneof.如:
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } }这时,在Oneof 里的全部字段不会包含 required, optional ,或repeated 类型说明符. 它们共用一个memory,所示能够节约内存.咱们可使用case()或WhichOneof()方法来查看哪个value被使用(使用哪个方法决定咱们使用的语言).
注意:当咱们为Oneof里的多个字段设置值时,只有最后一个被设置的字段被保留下来了.
注意它的后向兼容性。
Map的使用:
能够增长一个map:
map<key_type, value_type> map_field = N;其中,key_type,能够为任意的整数或者string类型, value_type,能够为任意的类型. 以下为一个例子,咱们把一个 Project的 Message类型与一个 string的value值相綁定了。
map<string, Project> projects = 3;
Packages:
它存在的目的是为了防止在.proto文件里的名字冲突. 就像C++里的命名空间差很少.以下所示:
首先咱们定义了一个这个:
package foo.bar; message Open { ... }而后再咱们使用它的时候,咱们要这么用:
message Foo { ... required foo.bar.Open open = 1; //看到没,咱们要加上它的package 的名字. ... }
另外,还有不少能够选的选项,见:https://developers.google.com/protocol-buffers/docs/proto.