protobuf是google提供的一个开源序列化框架。主要应用于通讯协议,数据存储中的结构化数据的序列化。它相似于XML,JSON这样的数据表示语言,其最大的特色是基于二进制,所以比传统的XML表示高效短小得多。虽然是二进制数据格式,但并无所以变得复杂,开发人员经过按照必定的语法定义结构化的消息格式,而后送给命令行工具,工具将自动生成相关的类,能够支持java、c++、python等语言环境。经过将这些类包含在项目中,能够很轻松的调用相关方法来完成业务消息的序列化与反序列化工做。html
下载地址:https://github.com/google/protobuf/releasesjava
==注意==: 若是在使用python时,用来编译
.proto
文件的protoc
的版本若是为2.X,那么可使用pip安装的protobuf,可是若是使用的protoc
的版本若是为3.X,那么须要卸载pip下载的protobuf,由于如今pip上的protobuf的版本还没升级到3.X,为2.X,版本不对应,会产生错误,相似于:python
serialized_pb=b'\n\ntest.proto\x12\x02lm\"2\n\nhelloworld\x12\n\n\x02id\x18\x01 \x02(\x05\x12\x0b\n\x03str\x18\x02 \x02(\t\x12\x0b\n\x03opt\x18\x03 \x01(\x05' TypeError: __init__() got an unexpected keyword argument 'syntax'
./autogen.sh ./configure make make check make install
直接下载编译好的realeases包。方便简单。咱们的主要目标是学会google protocal buffer,而不是编译它。linux
咱们将使用的示例是一个很是简单的“地址簿”应用程序,能够从一个文件中读写人们的联系方式。地址簿中的每一个人都有一个名字,一个ID、一个电子邮件地址,和联系电话号码。c++
为了建立你的“地址簿”应用,你会用到一个.proto文件。这是一个很简单的.proto
文件定义:你能够为你想序列化的数据结构添加一条Message,而后在Message中为每一个字段指定一个名称和一个类型。如下是你想为你的Message定义的.proto
文件,addressbook.proto
。git
package tutorial; 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]; } required PhoneNumber phone = 4; } message AddressBook{ required Person person = 1; }
在语法上很像C++和Java。那就让咱们看看文件中的每一个部分和看看它们到底是干什么的。github
.proto文件开头是包的声明,为了帮助防止在不一样的工程中命名冲突。在Python中,包一般由目录结构决定的,因此这个由你的.proto文件定义的包,在你生成你代码中是没有效果的。可是,你应该坚持声明这条语句,为了在protocol Buffers的命名空间中防止名子的冲突,就像其它非Python的语言那样。shell
类型 一个Message是一个包含一组类型字段的集合。 有许多简单的标准的数据类型能够用在类型字段中,包括bool,int32,float,double和string。你也可使用更加多的结构来定义你的Message。 例如用其它Message类型看成类型字段-在上面的例子PersonMessage中就包含了PhoneNumberMessage,还有AddressBookMessage包含PersonMessage。你也能够定义Message嵌入其它的Message——就如你所见到的那样,PhoneNumber类型就是在Person类型中定义的。你也能够定义一个枚举类型,若是你想你其中一个字段有一个预设的类型列表——在这里,你能够将你的电话号码列举为MOBILE,HOME或者WORK。 枚举类型的那个“=1”,“=2”标记每一个元素的识别,做为二进制编码中字段的惟一的标签。标签要求数字1-15比更高的数字少一个字节编码,因此,做为最优化的方案,你能够决定++对经常使用的和要重复使用的元素使用这些标签(1-15),把16或最高的数字留给不经常使用和可选择的元素++。每一个重复的字段里的元素要求从新编码它的标签号码,因此重复的字段特别适合使用这种优化。windows
修饰语 每一个字段必定要被如下的修饰语修饰:数组
==Required Is Forever== 你应该很是当心地把字段标记为
required
! 若是在某一时刻你但愿中止写或发送一个required
字段,那就把不肯定的字段更改成一个optinal
的字段——老版本的解释器会认为没有这个字段Message是不完整的,并且可能会无心中拒绝或删除它们。你应该考虑为你的buffer编写特定于应用程序的自定义验证例程。一些来自Google的软件工程师有这样的结论:使用required弊大于利;他们更愿意只用optional
和repeated
。可是,这一观点并不广泛。
如今你有了本身的.proto文件,下一件你须要去作的事就是生成你须要读写AddressBook(还带有Person和PhoneNumber)Message的类。为了完成这件工做,你须要运行protocol buffer 编译器protoc去编译你的.proto文件:
protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/addressbook.proto
由于你想生成Python的类,因此你要用--python_out
选项——也有相似的选项支持其它语言。 这样addressbook_pb2.py
就会生成在你指定的目标目录中。
生成的文件以下:
# Generated by the protocol buffer compiler. DO NOT EDIT! # source: addressbook.proto import sys _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='addressbook.proto', package='tutorial', syntax='proto2', serialized_pb=_b('\n\x11\x61\x64\x64ressbook.proto\x12\x08tutorial\"\xda\x01\n\x06Person\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\n\n\x02id\x18\x02 \x02(\x05\x12\r\n\x05\x65mail\x18\x03 \x01(\t\x12+\n\x05phone\x18\x04 \x02(\x0b\x32\x1c.tutorial.Person.PhoneNumber\x1aM\n\x0bPhoneNumber\x12\x0e\n\x06number\x18\x01 \x02(\t\x12.\n\x04type\x18\x02 \x01(\x0e\x32\x1a.tutorial.Person.PhoneType:\x04HOME\"+\n\tPhoneType\x12\n\n\x06MOBILE\x10\x00\x12\x08\n\x04HOME\x10\x01\x12\x08\n\x04WORK\x10\x02\"/\n\x0b\x41\x64\x64ressBook\x12 \n\x06person\x18\x01 \x02(\x0b\x32\x10.tutorial.Person') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) _PERSON_PHONETYPE = _descriptor.EnumDescriptor( name='PhoneType', full_name='tutorial.Person.PhoneType', filename=None, file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( name='MOBILE', index=0, number=0, options=None, type=None), _descriptor.EnumValueDescriptor( name='HOME', index=1, number=1, options=None, type=None), _descriptor.EnumValueDescriptor( name='WORK', index=2, number=2, options=None, type=None), ], containing_type=None, options=None, serialized_start=207, serialized_end=250, ) _sym_db.RegisterEnumDescriptor(_PERSON_PHONETYPE) _PERSON_PHONENUMBER = _descriptor.Descriptor( name='PhoneNumber', full_name='tutorial.Person.PhoneNumber', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='number', full_name='tutorial.Person.PhoneNumber.number', index=0, number=1, type=9, cpp_type=9, label=2, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='type', full_name='tutorial.Person.PhoneNumber.type', index=1, number=2, type=14, cpp_type=8, label=1, has_default_value=True, default_value=1, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto2', extension_ranges=[], oneofs=[ ], serialized_start=128, serialized_end=205, ) _PERSON = _descriptor.Descriptor( name='Person', full_name='tutorial.Person', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='name', full_name='tutorial.Person.name', index=0, number=1, type=9, cpp_type=9, label=2, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='id', full_name='tutorial.Person.id', index=1, number=2, type=5, cpp_type=1, label=2, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='email', full_name='tutorial.Person.email', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='phone', full_name='tutorial.Person.phone', index=3, number=4, type=11, cpp_type=10, label=2, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[_PERSON_PHONENUMBER, ], enum_types=[ _PERSON_PHONETYPE, ], options=None, is_extendable=False, syntax='proto2', extension_ranges=[], oneofs=[ ], serialized_start=32, serialized_end=250, ) _ADDRESSBOOK = _descriptor.Descriptor( name='AddressBook', full_name='tutorial.AddressBook', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='person', full_name='tutorial.AddressBook.person', index=0, number=1, type=11, cpp_type=10, label=2, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto2', extension_ranges=[], oneofs=[ ], serialized_start=252, serialized_end=299, ) _PERSON_PHONENUMBER.fields_by_name['type'].enum_type = _PERSON_PHONETYPE _PERSON_PHONENUMBER.containing_type = _PERSON _PERSON.fields_by_name['phone'].message_type = _PERSON_PHONENUMBER _PERSON_PHONETYPE.containing_type = _PERSON _ADDRESSBOOK.fields_by_name['person'].message_type = _PERSON DESCRIPTOR.message_types_by_name['Person'] = _PERSON DESCRIPTOR.message_types_by_name['AddressBook'] = _ADDRESSBOOK Person = _reflection.GeneratedProtocolMessageType('Person', (_message.Message,), dict( PhoneNumber = _reflection.GeneratedProtocolMessageType('PhoneNumber', (_message.Message,), dict( DESCRIPTOR = _PERSON_PHONENUMBER, __module__ = 'addressbook_pb2' # @@protoc_insertion_point(class_scope:tutorial.Person.PhoneNumber) )) , DESCRIPTOR = _PERSON, __module__ = 'addressbook_pb2' # @@protoc_insertion_point(class_scope:tutorial.Person) )) _sym_db.RegisterMessage(Person) _sym_db.RegisterMessage(Person.PhoneNumber) AddressBook = _reflection.GeneratedProtocolMessageType('AddressBook', (_message.Message,), dict( DESCRIPTOR = _ADDRESSBOOK, __module__ = 'addressbook_pb2' # @@protoc_insertion_point(class_scope:tutorial.AddressBook) )) _sym_db.RegisterMessage(AddressBook) # @@protoc_insertion_point(module_scope)
核心方法:
SerializeToString
:序列化消息并返回字符串;字符串是二进制形式。ParseFormatString
:从字符串的消息中解析出结构化的消息。#! /usr/bin/python # coding:utf-8 import addressbook_pb2 __author__ = 'hgf' def write(): person = addressbook_pb2.Person() person.name = "hgf" person.id = 1 person.email="hgf@bupt.edu.cn" phone = person.phone phone.number = "555-4321" phone.type = addressbook_pb2.Person.HOME handle = open('test.txt','wb') handle.write(person.SerializeToString()) handle.flush() handle.close() def read(): person = addressbook_pb2.Person() f = open("test.txt",'rb') person.ParseFromString(f.read()) f.close() print person.id print person.name print person.email print person.phone.number print person.phone.type #write() read()
==说明==:个人google protocol buffer 的版本是3.X的,官网上嵌套使用message的方式好像是
phone = person.phone.add()
,可是如今并无方法``
生成文件的截图:
{贺广福}(heguangfu)(tm) @2015-10-7 :laughing: