由于项目中要用到二进制存储数据,之前使用的方式是按照字节数依次将数据写入字节流中, 但是这样做起来做文件的协议兼容比较难做,所以我们考虑使用 protobuf 来做格式定义, 便于不同版本的文件兼容, 这边使用用起来十分方便且后续添加参数之后, 版本之间的改动很好处理,多出来的参数或者未找到的参数可以使用默认处理即可,十分方便
程序的配置文件是一个很常用的手段, 每次读取配置文件的信息, 容纳后根据参数决定我们的执行顺序, 是程序的一个很好的设计方式, 常用的配置文件我们会选择使用自己可读的文件格式,进行一定的程序注释之类的,然后在通过配置接口,将我们需要的参数依次读取到内存中,进行读写.
常用的配置文件格式有:
具体的参数区别与优劣比较自己可以查阅 常用配置文件格式
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) 软件最后安装的路径
CMakeLists.txt
的文件目录即可, 如: $(SOURCE)/protobuf-3.11.0/cmake
├─bin
├─examples
├─include
└─lib
假设我们需要测试 lib 文件, 建立 VS2015 工程, 新建 main.cpp
addressbook.proto
的文件
在我们使用编译好的库的时候, 需要引用 include 文件才能进行编译, $(INSTALL)/include
我们使用 protobuf 的例程文件里面的 addressbook.proto
, 配置相应的消息格式,
// 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]
.proto
文件编译成 对应的 .h
和 .cpp
文件protoc ./addressbook.proto --cpp_out=./
// 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.
由于我们需要才自定义文件的后续添加大量的数据文件, 所以无法使用这种方式, 可能还是要使用更多的文件版本的方式来处理文件兼容性问题吧, 后续会将自己的参数设计思路以及参数实现方法
原文:https://www.cnblogs.com/hugochen1024/p/12570745.html