Window+Protobuf使用说明

C++WindowCmakeProtocbuf

介绍

起因

由于项目中要用到二进制存储数据,之前使用的方式是按照字节数依次将数据写入字节流中, 但是这样做起来做文件的协议兼容比较难做,所以我们考虑使用 protobuf 来做格式定义, 便于不同版本的文件兼容, 这边使用用起来十分方便且后续添加参数之后, 版本之间的改动很好处理,多出来的参数或者未找到的参数可以使用默认处理即可,十分方便

常用配置文件介绍

程序的配置文件是一个很常用的手段, 每次读取配置文件的信息, 容纳后根据参数决定我们的执行顺序, 是程序的一个很好的设计方式, 常用的配置文件我们会选择使用自己可读的文件格式,进行一定的程序注释之类的,然后在通过配置接口,将我们需要的参数依次读取到内存中,进行读写.

常用的配置文件格式有:

  • xml
  • yaml
  • json
  • ini
  • properties
  • ......

具体的参数区别与优劣比较自己可以查阅 常用配置文件格式

protocbuf 介绍

Protocol buffers 是一种语言中立,平台无关,可扩展的序列化数据的格式,可用于通信协议,数据存储等。

Protocol buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。

Protocol buffers 很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

使用说明

环境列表

本次使用的环境列表如下

本机配置开源库编译

由于是在 Windows 下面编译, 所以这边使用了 cmake-gui 的编译方式, 按照如下步骤配置

0. 假设已经新建了一个文件夹,后续的操作都在此文件夹下 假设操作的所有文件夹都在 D:/Soft/Protobuf 文件夹下面, 新建三个文件夹,便于后续操作, 我们使用 $() 代替在你自己设备中的路径, 不要弄错

* source $(SOURCE) 原始文件

* build $(BUILD) 工程文件和编译文件

* install $(INSTALL) 软件最后安装的路径

  1. 下载原始项目,如果压缩包,将全部文件解压到 $(SOURCE) 文件夹下面
  2. 使用 cmake-gui 配置相应的原始文件夹, 一般打开到 原始文件夹下有 CMakeLists.txt的文件目录即可, 如: $(SOURCE)/protobuf-3.11.0/cmake
  3. 目标目录打开到 $(BUILD)
  4. 点击左下角,Configure, 选择 自己本机的 VS版本 可以选择架构 例如: VS2015 x64 架构
  5. 再次点击 Configure ,程序会开始读取 CMakeLists 里面的参数, 输出窗口会输出log 信息, 如果有红色会报错,我们需要排查相应的库是否完成依赖项,如果有重要依赖, 可能无法完成编译
  6. 如果一切无措,我们可以看到 如图的cmake 编译参数, 这是简略参数, 根据具体内容, 我们勾选相应的参数并设置相应的安装路径即可完成配置,
  7. 再次点击配置, 程序会进行配置, 选项会取消红色, 等生成完成即可,
  8. 点击 Generate 生成 VS2015 工程, 我们可以选择 open Project 快速打开,或者到 $(BUILD) 文件夹 点击对应的 sln 文件打开项目

Cmake 编译参数

使用 VS2015 编译库

  1. 一般 我们打开之后, 选择 Debug 或者 Release 选择 生成-生成解决方案, VS就开始了编译过程, 开始进行了项目编译过程
  2. 可能存在编译错误, 一般不会出现, 不建议更改源项目, 可以查看是否自己错误
  3. 编译好的之后的安装文件夹可能存在如下的文件夹结构, 将 $(INSTALL)/bin 加入到 path 环境中, 里面有 dll 文件和 exe 文件便于使用
    ├─bin
├─examples
├─include
└─lib

使用编译好的库 lib

  1. 假设我们需要测试 lib 文件, 建立 VS2015 工程, 新建 main.cpp addressbook.proto 的文件

  2. 在我们使用编译好的库的时候, 需要引用 include 文件才能进行编译, $(INSTALL)/include

  3. 我们使用 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]
  1. 在工作目录下打开命令行, 使用bash 执行命令 将 .proto 文件编译成 对应的 .h.cpp 文件
protoc ./addressbook.proto --cpp_out=./
  1. 将样例里面的 列出人员和新加人员复制到自定义文件中
根据测试样例改写的 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;
}
  1. 配置 main 函数的输入参数

    项目--右键--属性--调试--命令参数----加入 ./test.bin

  2. 配置 C++ 项目 include 文件夹和附加库

    项目--右键--属性--VC++目录--包含目录--编辑-----加入 $(INSTALL)/include

    项目--右键--属性--链接器--输入--附加依赖项-- 编辑-------加入 $(INSTALL)/lib/libprotobuf.lib 和 $(INSTALL)/lib/libprotobufd.lib

  3. 如果编译了 共享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.

更多

由于我们需要才自定义文件的后续添加大量的数据文件, 所以无法使用这种方式, 可能还是要使用更多的文件版本的方式来处理文件兼容性问题吧, 后续会将自己的参数设计思路以及参数实现方法

参考链接

  1. C++ 程序员 Protocol Buffers 基础指南
  2. Protocol Buffer Basics: C++
  3. Google Protocol Buffer 的使用和原理
  4. 高效的数据压缩编码方式 Protobuf
  5. Protobuf 终极教程
  6. protobuf【1】详细的安装和使用(windows cpp)

Window+Protobuf使用说明的更多相关文章

  1. protobuf使用说明

    1..proto文件为要生成.java文件的模板文件,其中包含名称空间.文件名等信息2.cmd中进入当前目录D:\JAVA\protoc-2.5.0-win323.运行 protoc.exe --ja ...

  2. C 简易基础开发框架 - simple c

    引言   一个为 简单高效而生的 简易跨平台的 纯C开发框架.   githup上源码   https://github.com/wangzhione/sconsole_project 请容我细说 s ...

  3. 移动端布局 - REM方式

    默认以宽度为640px的设计稿为基准页面,然后通过JS获取当前显示设备的尺寸,对应的调整 html 标签的font-size大小,从而实现通过以rem为单位的移动端布局适配. 具体代码 (functi ...

  4. window下编译并使用google protobuf

    参考网址: http://my.oschina.net/chenleijava/blog/261263 http://www.ibm.com/developerworks/cn/linux/l-cn- ...

  5. window 安装 Protobuf

    环境安装 1:下载CMake 2:打开VS Command Prompt 3:修改工作目录到目标目录 cd C:\Path\to 4:创建编译完后 protobuf headers/libraries ...

  6. window.location.hash 使用说明

    本文给大家详细汇总了关于window.location.hash的知识点,属性以及用法等等,非常的实用,并附上了例子,有需要的小伙伴可以参考下.   location是javascript里边管理地址 ...

  7. window go protobuf

    http://studygolang.com/articles/8804 protoc --go_out=. protocol.proto E:\TEST\TESTGRPC\src\google.go ...

  8. window下golang包管理glide使用说明

    golang是一门简洁高效的开发 语言,但是包管理一直是一个痛点,如图 : 很多开源项目特别是github.com中的大量应用golang.org和google.golang.org中的源码,且由于被 ...

  9. (转)window.location.hash 属性使用说明

    location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url.而location. ...

随机推荐

  1. 广告行业中那些趣事系列8:详解BERT中分类器源码

    最新最全的文章请关注我的微信公众号:数据拾光者. 摘要:BERT是近几年NLP领域中具有里程碑意义的存在.因为效果好和应用范围广所以被广泛应用于科学研究和工程项目中.广告系列中前几篇文章有从理论的方面 ...

  2. DAO,Service,Controler的简介

    DAO层: DAO层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表的增删改查,也就是说某个DAO一定是和数据库的某一张表一一对应的,其中封 ...

  3. docker中MySQL镜像数据,日志,配置持久化

    Docker的MySQL8镜像, 实行数据持久化 version: '3.1' services: mysql-itoken-service-admin: restart: always image: ...

  4. 存储机制 cookie session jwt token

    cookieCookie的诞生 由于HTTP协议是无状态的,而服务器端的业务必须是要有状态的.Cookie诞生的最初目的是为了存储web中的状态信息,以方便服务器端使用.比如判断用户是否是第一次访问网 ...

  5. 使用mpvue开发小程序教程

    从vue到mpvue再到微信小程序,这么几天下来感觉被搞晕了.三者之间的很多功能存在差异,项目也快接近尾声了,坑也踩了很多了,现在给后来的你们一点总结性经验: 1. 在模板中,动态插入HTML的v-h ...

  6. Linux服务器架设篇,DHCP服务器的搭建

    学习之前,我们首先来看一个案例: 假如你是一个学校的网络管理老师,需要为教室的70多台电脑配置好网络你会怎么办? 一台一台的给他们配置? 在这里我特别欣赏鸟哥的一句话--"当管理员最大的幸福 ...

  7. 用robotframework 标准库String解决由于存在千分位分隔符导致两个数值不相等的问题。

    在编写robotframework自动化断言的过程中,我遇到了如下问题: 我想写一个两个金额判断是否相等的断言,其中一个金额是展示字段存在千分位分隔符,另一个金额是input带入字段,没有千分位分隔符 ...

  8. String 对象-->substring() 方法

    1.定义和用法 substring() 方法用于提取两个指定下标之间的字符. substring() 方法返回的子串包括 开始 处的字符,但不包括 结束 处的字符 语法: string.substri ...

  9. hadoop(十一)HDFS简介和常用命令介绍

    HDFS背景 随着数据量的增大,在一个操作系统中内存不了了,就需要分配到操作系统的的管理磁盘中,但是不方便管理者维护,迫切需要一种系统来管理多态机器上的文件,这就是分布式文件管理系统. HDFS的概念 ...

  10. Linux环境安装Docker

    1. 使用APT安装 # 更新数据源 apt-get update # 安装所需依赖 apt-get -y install apt-transport-https ca-certificates cu ...