视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
ProtocolBuffers的基础说明和使用
2020-11-09 14:10:12 责编:小采
文档


我们开始需要使用protobuf的原因是,在处理OJ的contest模块的时候,碰到一个问题就是生成contestRank的时候,需要存储很多信息。如果我们采用model存储的话,那么一方面后续如果继续加入其他信息的话修改会灰常麻烦,另一方面就是实现比较复杂,因为对于rank

我们开始需要使用protobuf的原因是,在处理OJ的contest模块的时候,碰到一个问题就是生成contestRank的时候,需要存储很多信息。如果我们采用model存储的话,那么一方面后续如果继续加入其他信息的话修改会灰常麻烦,另一方面就是实现比较复杂,因为对于rank来说,每一条rank的主键首先是UserID,其次存储的基本信息有AC数,题目AC情况,罚时等等,其中题目AC情况又包括以题目ID为主键,属性(是否AC,AC时间,罚时,这道题目的提交统计),而每一道题目的提交统计又是一个submit实体,这样的话,就会有多级嵌套存在。如果后续添加的话会更多。还有一个原因就是存储也不方便,逻辑构思不是很明确,需要注意保存各种信息,实现复杂。

protobuf的主要机理是把存储的信息序列化为一个字符串存储,需要信息的时候又可以逆序列化把原始信息还原出来,而且可以实现多级嵌套的属性。所以就尝试使用这个来解决rank的存储(/ □ \)

1:protobuf是什么?

好吧,这个有百科,see see:protocolbuffer是google 的一种数据交换的格式,它于语言,于平台。google 提供了三种语言的实现:java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。

简单地说,就是把某种数据结构的信息,以某种格式保存起来。主要用于数据存储、传输协议格式等场合。

2:下载安装:

地址:http://code.google.com/p/protobuf/downloads/list

安装:

 tar -xzf protobuf-2.1.0.tar.gz 
 cd protobuf-2.1.0 
 ./configure 
 make 
 make check 
 make install

3:使用建立

网上有各种例子,比如简单的book的书写等等都可以用来练习操作

首先,首先我们需要编写一个 proto 文件,定义我们程序中需要处理的结构化数据,在 protobuf 的术语中,结构

化数据被称为 Message。例如:

package contest_submit;
 message ContestSubmit {
 required int32 id = 1;
 required int timestamp = 2;
 }

上述例子中id是int32的,timestamp是int的。required代表这个是必须的,有点像数据库里的not null,还有一

种就是optional,即可选的。

编译.proto文件,命令如下:

protoc -I=./ --python_out=./ ./*.proto

代表的意思是输入的.proto文件在当前目录,输出生成的编译文件到当前目录,源文件是所有以.proto结尾的文件

。网上完整的格式是:

假设您的 proto 文件存放在 $SRC_DIR 下面,您也想把生成的文件放在同一个目录下,则可以使用如下命令:
 protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/文件明.proto

编译完成之后会生成 contest_submit_pb2.py文件(对于上面的例子来说)

当然,protobuf也是可以嵌套的。即可以在一个类中声明另一个类的对象,例如,rank的实现:

package contest_rank;
 message Submit {
 optional string status = 1;
 optional string date_time = 2;
 optional string runID = 3;
 }
 
 message Problem {
 optional int32 problemID = 1;
 repeated Submit submit = 2;
 optional int32 acindex = 3;
 optional int32 totalindex = 4;
 optional int32 time = 5;
 optional int32 FB = 6;
 }
 
 message Rank {
 optional string userID = 1;
 optional string contestID = 2;
 repeated Problem problem = 3;
 optional int32 penalty = 4;
 optional int32 ac = 5;
 optional int32 total = 6;
 }
 
 message ContestRankList {
 optional string contestID = 1;
 repeated Rank rank = 2;
 }
这样的话,就可以实现我们前面的需求即多级信息嵌套了。

使用:

首先,要记得载入头文件from 文件夹.proto import rank_pb2

对于实现rank的存储的时候,首先声明一个rank类,以便后续对数据的处理

class Rank():
 class Problem():
 class Submit():
 def __init__(self, runID = "", status="", date_time=""):
 self.status = status
 self.date_time = date_time
 self.runID = runID
 
 def __init__(self, problemID):
 self.problemID = problemID
 self.submit_list = []
 self.acindex = 0
 self.totalindex = 0
 self.time = 0
 self.FB = 0
 
 def add_submit(self, submit):
 self.submit_list.append(submit)
 
 def __init__(self, userID, contestID):
 self.userID = userID
 self.contestID = contestID
 self.problem_list = {}
 self.ac = 0
 self.penalty = 0
 self.total = 0
 
当我们得到rank_list的时候(rank_list是一个存放rank的字典),首先声明一个proto的载体:

contest_rank_list = rank_pb2.ContestRankList()
然后填充信息,比如先把比赛的ID加入,即contest_rank_list.contestID = contestID。然后按照protobuf的结构,依

次把需要的信息填入。对于嵌套的实体来说,每次声明的时候可以使用add()方法,比如在一个比赛中,会有多个用户

的rank信息存在,所以按照用户ID来添加信息:

rank_proto = contest_rank_list.rank.add()
rank.load_data_to_proto(rank_proto)
其中的load_data_to_proto是rank类的一个方法,内容就是把信息加入到protobuf里面而已:

def load_data_to_proto(self, rank):
 rank.userID = self.userID
 rank.contestID = self.contestID
 rank.ac = self.ac
 rank.penalty = self.penalty
 rank.total = self.total
 
 for problemID in self.problem_list:
 problem = self.problem_list[problemID]
 p = rank.problem.add()
 p.problemID = problem.problemID
 p.acindex = problem.acindex
 p.totalindex = problem.totalindex
 p.time = problem.time
 p.FB = problem.FB
 for submit in problem.submit_list:
 s = p.submit.add()
 s.status = submit.status
 s.date_time = submit.date_time
 s.runID = submit.runID
 return rank
 
这样我们就把数据存入了我们声明的protobuf里面了,然后就是存储的时候序列化,比如我们存储到SSDB里面即:

ssdb_api.SetContestRankListProto(contestID, contest_rank_list.SerializeToString())

至此,信息已经存入数据库,那么接下来就是如何读出了:

对于得到我们存储的rank信息来说,即:

 contest_rank_list = ssdb_api.GetContestRankListProto(contest_id)
 rank_list = rank_pb2.ContestRankList()
 rank_list.ParseFromString(contest_rank_list)
即,再次声明一个protobuf的对象,然后逆序列化,就得到了我们需要的信息,简单吧(/ □ \)。

后面需要的数据,就用父名.子名访问即可。




下载本文
显示全文
专题