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: