Google Protocol Buffer简介(一)
来源:互联网 发布:暴风雪数据 编辑:程序博客网 时间:2024/06/05 02:12
在阅读Chromium Remoting源代码时,遇到了.proto
文件。经过搜索,在Google Code上找到了对这种文件的详细说明:Protocol Buffer。这里,对这个机制做一简介。本文将以Python语言为例,介绍:
.proto
文件的格式- 使用Protocol Buffer编译器
- 使用Python语言的Protocol Buffer API来读写消息
更多内容,请参阅Protocol Buffer官方网站。
为什么要用Protocol Buffer?
对于结构化的数据,我们有很多方式来序列化/反序列化:
- 使用原始的二进制数据:即使用数据在内存中的映射作为数据流,但这种方式的兼容性非常差。
- 使用自定义的编码方式:例如,用字符串“12:3:-23:67”的方式保存这4个整数,对于简单的数据,这种方式还是比较好用的 。
- 使用XML进行序列化:这种方法兼容性很好,但显而易见的是,XML的开销很大。
相比之下,Protocol Buffer看起来要灵活很多。只需编写.proto
文件来描述数据结构,即可使用Google提供的框架来完成数据的序列化/反序列化。此外,Protocol Buffer格式还支持数据的扩展,即使增加了新的域,通常也能够保证兼容性。
定义一个协议(Protocol)
这里以一个电话本程序为例,设计一个.proto
文件。定义一个.proto
文件很简单:对每种需要序列化的数据结构编写一个message,然后给每个字段指明名称和类型即可。本例如下:
package tutorial;message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType {GO MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4;}message AddressBook { repeated Person person = 1;}
.proto
文件首先由package声明开始,用于防止项目间的名称冲突。在Python中,这个就不那么重要了,因为Python是以目录结构作为包划分。但是,为了跨语言访问,即使用Python,还是应该在文件的开始部分声明package。
接下来,我们定义了一个message。message是一些域的集合,域的类型可以是简单的数据类型,例如bool
,int32
,float
,double
以及string
。域的类型还可以是复杂类型,例如上例中的Person
类型的message中包含了PhoneNumber
类型的message。此外,也可以定义枚举(enum
)类型,例如这里的PhoneType
。
“=1”,“=2”这样的记号是每个字段唯一的标记。简单来说,在传输时,传输的数据是这个标记,而不是域的名字。1-15在编码时使用较少的字节数,因此,对于常用的域,应当考虑使用1-15作为标记。
每个域必须包含一下修饰符之一:
required
:字段值不能为空,否则会产生异常。optional
:字段值如果没有设置,则使用默认值,如果没有指定默认值,则使用系统默认值,例如0,空字符串等。repeated
:字段可以重复出现任意自然数次。值的顺序会被保留。
关于.proto
文件写法的完整规则,请参见Protocol Buffer Language Guide。
编译Protocol Buffer
完成.proto
文件后,接下来需要生成可以读写AddressBook
的类。这一工作通过使用protoc
程序完成:
- 安装编译器。下载安装包,按照README的提示操作。
- 运行编译器:
protoc --python_out=. addressbook.proto
这样会在当前目录下生成addressbook_pb2.py
文件。
Protocol Buffer API
Python版的编译器并不生成读写数据的代码,而是生成message的描述符,通过Google提供的库来访问。
可以这样使用Protocol Buffer:
import addressbook_pb2person = addressbook_pb2.Person()person.id = 1234person.name = "John Doe"person.email = "jdoe@example.com"phone = person.phone.add()phone.number = "555-4321"phone.type = addressbook_pb2.Person.HOME
枚举类型
枚举类型的值和.proto
文件中指定的值一样。因此,以addressbook_pb2.Person.WORK
为例,其值为2。
Message的一般方法
IsInitialized()
:检查所有的required字段是否都已赋值CopyFrom(other_msg)
:用other_msg替换当前的messageClear()
:清空message
更多信息,请参见Message的API文档。
解析和序列化
SerializeToString()
:将message序列化,并得到一个字符串。注意,这里的字符串只作为二进制数据的容器。ParseFromString(data)
:把字符串解析为message。
最后,以两个例子(写、读message)作为本文的结束:
#! /usr/bin/pythonimport addressbook_pb2import sys# This function fills in a Person message based on user input.def PromptForAddress(person): person.id = int(raw_input("Enter person ID number: ")) person.name = raw_input("Enter name: ") email = raw_input("Enter email address (blank for none): ") if email != "": person.email = email while True: number = raw_input("Enter a phone number (or leave blank to finish): ") if number == "": break phone_number = person.phone.add() phone_number.number = number type = raw_input("Is this a mobile, home, or work phone? ") if type == "mobile": phone_number.type = addressbook_pb2.Person.MOBILE elif type == "home": phone_number.type = addressbook_pb2.Person.HOME elif type == "work": phone_number.type = addressbook_pb2.Person.WORK else: print "Unknown phone type; leaving as default value."# Main procedure: Reads the entire address book from a file,# adds one person based on user input, then writes it back out to the same# file.if len(sys.argv) != 2: print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" sys.exit(-1)address_book = addressbook_pb2.AddressBook()# Read the existing address book.try: f = open(sys.argv[1], "rb") address_book.ParseFromString(f.read()) f.close()except IOError: print sys.argv[1] + ": Could not open file. Creating a new one."# Add an address.PromptForAddress(address_book.person.add())# Write the new address book back to disk.f = open(sys.argv[1], "wb")f.write(address_book.SerializeToString())f.close()
#! /usr/bin/pythonimport addressbook_pb2import sys# Iterates though all people in the AddressBook and prints info about them.def ListPeople(address_book): for person in address_book.person: print "Person ID:", person.id print " Name:", person.name if person.HasField('email'): print " E-mail address:", person.email for phone_number in person.phone: if phone_number.type == addressbook_pb2.Person.MOBILE: print " Mobile phone #: ", elif phone_number.type == addressbook_pb2.Person.HOME: print " Home phone #: ", elif phone_number.type == addressbook_pb2.Person.WORK: print " Work phone #: ", print phone_number.number# Main procedure: Reads the entire address book from a file and prints all# the information inside.if len(sys.argv) != 2: print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" sys.exit(-1)address_book = addressbook_pb2.AddressBook()# Read the existing address book.f = open(sys.argv[1], "rb")address_book.ParseFromString(f.read())f.close()ListPeople(address_book)
参考文献
Protocol Buffer Basics: Python
- Google Protocol Buffer简介(一)
- Google Protocol Buffer简介(一)
- Google protocol buffer简介
- Google Protocol Buffer简介(二)
- Google Protocol Buffer
- Protocol Buffer(Google) 學習筆記一
- google protocol buffer
- Google Protocol Buffer
- Google Protocol Buffer
- Google Protocol Buffer
- Google Protocol Buffer 用法
- 【Google Protocol Buffer】概览
- Google Protocol Buffer 学习
- Google Protocol Buffer 学习
- Google Protocol Buffer
- Google Protocol Buffer 介绍
- google protocol buffer基础
- Google Protocol Buffer
- 反汇编
- 同学,我大四了! ——刘新宇(百度贴吧:季节的阁子)
- Tomcat从零开始(六)容器
- Oracle数据库管理员经常使用的表和视图
- 简易计算器
- Google Protocol Buffer简介(一)
- 一个老程序员的建议
- 阿里巴巴招聘研发工程师
- android开发新浪微博客户端 完整攻略
- 千万别用数据库关键字做字段名
- 新浪微博、腾讯微博开放平台整合DEMO分享
- ShellExecute 的使用
- 数组
- linux头文件的查找方式和库的搜索路径