Window+Protobuf使用说明
Window+Protobuf使用说明
介绍
起因
由于项目中要用到二进制存储数据,之前使用的方式是按照字节数依次将数据写入字节流中, 但是这样做起来做文件的协议兼容比较难做,所以我们考虑使用 protobuf 来做格式定义, 便于不同版本的文件兼容, 这边使用用起来十分方便且后续添加参数之后, 版本之间的改动很好处理,多出来的参数或者未找到的参数可以使用默认处理即可,十分方便
常用配置文件介绍
程序的配置文件是一个很常用的手段, 每次读取配置文件的信息, 容纳后根据参数决定我们的执行顺序, 是程序的一个很好的设计方式, 常用的配置文件我们会选择使用自己可读的文件格式,进行一定的程序注释之类的,然后在通过配置接口,将我们需要的参数依次读取到内存中,进行读写.
常用的配置文件格式有:
- xml
- yaml
- json
- ini
- properties
- ......
具体的参数区别与优劣比较自己可以查阅 常用配置文件格式
protocbuf 介绍
Protocol buffers 是一种语言中立,平台无关,可扩展的序列化数据的格式,可用于通信协议,数据存储等。
Protocol buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。
Protocol buffers 很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
使用说明
环境列表
本次使用的环境列表如下
- protobuf 3.11.0 protobuf-all-3.11.0.zip
- cmake 3.14.2
- VS2015
本机配置开源库编译
由于是在 Windows 下面编译, 所以这边使用了 cmake-gui 的编译方式, 按照如下步骤配置
0. 假设已经新建了一个文件夹,后续的操作都在此文件夹下 假设操作的所有文件夹都在 D:/Soft/Protobuf 文件夹下面, 新建三个文件夹,便于后续操作, 我们使用 $() 代替在你自己设备中的路径, 不要弄错
* source $(SOURCE) 原始文件
* build $(BUILD) 工程文件和编译文件
* install $(INSTALL) 软件最后安装的路径
- 下载原始项目,如果压缩包,将全部文件解压到 $(SOURCE) 文件夹下面
- 使用 cmake-gui 配置相应的原始文件夹, 一般打开到 原始文件夹下有
CMakeLists.txt的文件目录即可, 如:$(SOURCE)/protobuf-3.11.0/cmake - 目标目录打开到 $(BUILD)
- 点击左下角,Configure, 选择 自己本机的 VS版本 可以选择架构 例如: VS2015 x64 架构
- 再次点击 Configure ,程序会开始读取 CMakeLists 里面的参数, 输出窗口会输出log 信息, 如果有红色会报错,我们需要排查相应的库是否完成依赖项,如果有重要依赖, 可能无法完成编译
- 如果一切无措,我们可以看到 如图的cmake 编译参数, 这是简略参数, 根据具体内容, 我们勾选相应的参数并设置相应的安装路径即可完成配置,
- 再次点击配置, 程序会进行配置, 选项会取消红色, 等生成完成即可,
- 点击 Generate 生成 VS2015 工程, 我们可以选择 open Project 快速打开,或者到 $(BUILD) 文件夹 点击对应的 sln 文件打开项目

使用 VS2015 编译库
- 一般 我们打开之后, 选择 Debug 或者 Release 选择 生成-生成解决方案, VS就开始了编译过程, 开始进行了项目编译过程
- 可能存在编译错误, 一般不会出现, 不建议更改源项目, 可以查看是否自己错误
- 编译好的之后的安装文件夹可能存在如下的文件夹结构, 将 $(INSTALL)/bin 加入到 path 环境中, 里面有 dll 文件和 exe 文件便于使用
├─bin
├─examples
├─include
└─lib
使用编译好的库 lib
假设我们需要测试 lib 文件, 建立 VS2015 工程, 新建
main.cppaddressbook.proto的文件在我们使用编译好的库的时候, 需要引用 include 文件才能进行编译,
$(INSTALL)/include我们使用 protobuf 的例程文件里面的
addressbook.proto, 配置相应的消息格式,
自定义 message 格式
// See README.txt for information and build instructions.
//
// Note: START and END tags are used in comments to define sections used in
// tutorials. They are not part of the syntax for Protocol Buffers.
//
// To get an in-depth walkthrough of this file and the related examples, see:
// https://developers.google.com/protocol-buffers/docs/tutorials
// [START declaration]
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
// [END declaration]
// [START java_declaration]
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
// [END java_declaration]
// [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
// [END csharp_declaration]
// [START messages]
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
// Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
}
// [END messages]
- 在工作目录下打开命令行, 使用bash 执行命令 将
.proto文件编译成 对应的.h和.cpp文件
protoc ./addressbook.proto --cpp_out=./
- 将样例里面的 列出人员和新加人员复制到自定义文件中
根据测试样例改写的 Main.cpp
// See README.txt for information and build instructions.
#include <ctime>
#include <fstream>
#include <google/protobuf/util/time_util.h>
// #include <google/protobuf/>
#include <iostream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
using google::protobuf::util::TimeUtil;
// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook& address_book) {
for (int i = 0; i < address_book.people_size(); i++)
{
const tutorial::Person& person = address_book.people(i);
cout << "Person ID: " << person.id() << endl;
cout << " Name: " << person.name() << endl;
if (person.email() != "")
{
cout << " E-mail address: " << person.email() << endl;
}
for (int j = 0; j < person.phones_size(); j++)
{
const tutorial::Person::PhoneNumber& phone_number = person.phones(j);
switch (phone_number.type())
{
case tutorial::Person::MOBILE:
cout << " Mobile phone #: ";
break;
case tutorial::Person::HOME:
cout << " Home phone #: ";
break;
case tutorial::Person::WORK:
cout << " Work phone #: ";
break;
default:
cout << " Unknown phone #: ";
break;
}
cout << phone_number.number() << endl;
}
if (person.has_last_updated())
{
cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << endl;
}
}
}
// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n');
cout << "Enter name: ";
getline(cin, *person->mutable_name());
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}
while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
}
tutorial::Person::PhoneNumber* phone_number = person->add_phones();
phone_number->set_number(number);
cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(tutorial::Person::MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
*person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(nullptr));
}
// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// 列出文件 读取的人员
ListPeople(address_book);
// 重新添加一个人员
PromptForAddress(address_book.add_people());
{
// Write the new address book back to disk.
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
配置 main 函数的输入参数
项目--右键--属性--调试--命令参数----加入 ./test.bin配置 C++ 项目 include 文件夹和附加库
项目--右键--属性--VC++目录--包含目录--编辑-----加入 $(INSTALL)/include
项目--右键--属性--链接器--输入--附加依赖项-- 编辑-------加入 $(INSTALL)/lib/libprotobuf.lib 和 $(INSTALL)/lib/libprotobufd.lib如果编译了 共享dll 文件
项目--右键--属性--C/C++--预处理器--预处理器定义--编辑----加入 PROTOBUF_USE_DLLS
最终编译成功, 可以列出来 文件中的参数, 最终生成的二进制文件hex 文件内容

Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000: 0A 15 0A 01 32 10 01 1A 01 33 22 03 0A 01 34 2A ....2....3"...4*
00000010: 06 08 B2 AD F9 EE 05 0A 15 0A 01 32 10 01 1A 01 ..2-yn.....2....
00000020: 33 22 03 0A 01 34 2A 06 08 D2 AD F9 EE 05 3"...4*..R-yn.
更多
由于我们需要才自定义文件的后续添加大量的数据文件, 所以无法使用这种方式, 可能还是要使用更多的文件版本的方式来处理文件兼容性问题吧, 后续会将自己的参数设计思路以及参数实现方法
参考链接
Window+Protobuf使用说明的更多相关文章
- protobuf使用说明
1..proto文件为要生成.java文件的模板文件,其中包含名称空间.文件名等信息2.cmd中进入当前目录D:\JAVA\protoc-2.5.0-win323.运行 protoc.exe --ja ...
- C 简易基础开发框架 - simple c
引言 一个为 简单高效而生的 简易跨平台的 纯C开发框架. githup上源码 https://github.com/wangzhione/sconsole_project 请容我细说 s ...
- 移动端布局 - REM方式
默认以宽度为640px的设计稿为基准页面,然后通过JS获取当前显示设备的尺寸,对应的调整 html 标签的font-size大小,从而实现通过以rem为单位的移动端布局适配. 具体代码 (functi ...
- window下编译并使用google protobuf
参考网址: http://my.oschina.net/chenleijava/blog/261263 http://www.ibm.com/developerworks/cn/linux/l-cn- ...
- window 安装 Protobuf
环境安装 1:下载CMake 2:打开VS Command Prompt 3:修改工作目录到目标目录 cd C:\Path\to 4:创建编译完后 protobuf headers/libraries ...
- window.location.hash 使用说明
本文给大家详细汇总了关于window.location.hash的知识点,属性以及用法等等,非常的实用,并附上了例子,有需要的小伙伴可以参考下. location是javascript里边管理地址 ...
- window go protobuf
http://studygolang.com/articles/8804 protoc --go_out=. protocol.proto E:\TEST\TESTGRPC\src\google.go ...
- window下golang包管理glide使用说明
golang是一门简洁高效的开发 语言,但是包管理一直是一个痛点,如图 : 很多开源项目特别是github.com中的大量应用golang.org和google.golang.org中的源码,且由于被 ...
- (转)window.location.hash 属性使用说明
location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url.而location. ...
随机推荐
- 牛客寒假基础集训营 | Day1 J题—u's的影响力(水题)
Day1 J题-u's的影响力 有一天,kotori发现了一个和lovelive相似的游戏:bangdream.令她惊讶的是,这个游戏和lovelive居然是同一个公司出的! kotori经过一段时间 ...
- Spring (六):整合Mybatis
本文是按照狂神说的教学视频学习的笔记,强力推荐,教学深入浅出一遍就懂!b站搜索狂神说或点击下面链接 https://space.bilibili.com/95256449?spm_id_from=33 ...
- mpvue小程序开发
查阅资料,看官方文档,知道mpvue是一个使用 Vue.js 开发小程序的前端框架(美团的开源项目).框架基于 Vue.js 核心,mpvue 修改了 Vue.js 的 runtime 和 compi ...
- android注册验证码的使用
主要是创建了验证码的生成类. 通过此生成类,与imageview相互联系起来,实现验证码显示.并添加点击事件,实现验证码的切换. 实验的截图如下:(验证码可以点击切换) 具体的关于验证码的生成类如下: ...
- go中的线程的实现模型-P G M的调度
线程实现模型 go中线程的实现是依靠 P G M M machine的缩写.一个M代表一个内核线程,或称“工作线程” P processor的缩写.一个P代表执行一个Go代码片段所需要的资源(或称“上 ...
- 004-流程控制-C语言笔记
004-流程控制-C语言笔记 学习目标 1.[掌握]关系运算符和关系表达式 2.[掌握]逻辑运算符和逻辑表达式 3.[掌握]运算符的优先级和结合性 4.[掌握]if-else if-else结构的使用 ...
- 软件包管理rpm和yum
rpm的使用: 安装的包相关包信息会保存在/var/lib/rpm目录下的文件中 安装参数: -i install安装 -v 显示详细信息 -h 打印####号 -V 校验软件包,会到/var/lib ...
- el-tab-pane label的文字内容怎样设间距
el-tab-pane label的文字内容怎样设间距 问题描述: 在使用element-ui的el-tab-pane做标签页时,label属性的位置与样式不能通过style样式直接解决 百度后几乎没 ...
- Python算法题:金字塔
代码如下: #Python金字塔练习 """ 最大层数:max_level 当前层数:current_level 金字塔正序时: 每层的空格=最大层数-当前层数 每层的星 ...
- python机器学习入门-(1)
机器学习入门项目 如果你和我一样是一个机器学习小白,这里我将会带你进行一个简单项目带你入门机器学习.开始吧! 1.项目介绍 这个项目是针对鸢尾花进行分类,数据集是含鸢尾花的三个亚属的分类信息,通过机器 ...