赞
踩
假如我们有一个问题是关于:存储一个人的名字(name)以及唯一表示符(id)和邮箱(email)以及它的电话号码(number)和此电话号码所在的类型(PhoneType),主人的性别(sex)。并且需要将其保存在二进制文件中或者txt文件中,如果需要还需要将其从二进制文件中或者txt文件中读取,我们如何使用protobuf去实现它呢?
首先我们需要创建一个addressbook.proto文件,文件中的内容为:
syntax = "proto2";
package tutorial;
//把proto文件转化成c++代码.执行下面命令.
//protoc --cpp_out=. addressbook.proto
message Person {
required string name = 1;
required int32 id = 2;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
enum sex_people {
WOMAN = 0;
MAN = 1;
}
message PhoneNumber {
optional sex_people sex = 1 [default = MAN];
optional PhoneType type = 2 [default = HOME];
required string number = 3;
}
repeated PhoneNumber phones = 3;
optional string email = 4;
optional sex_people sex = 5 [default = MAN];
optional PhoneType type = 6 [default = HOME];
}
message AddressBook {
repeated Person people = 1;
}
message表示消息,相当于c++中的一个类Person。required表示必须字段,而optional表示可选字段,repeated表示可重复字段(每一个人都有多个电话号码),这些关键字紧接着的关键字(string int32 etc.)为我们声明的变量的类型。
在address.proto 同级目录下打开终端使用下面命令将其转化成addressbook.pb.h 和address.pb.cc 文件
protoc --cpp_out=. addressbook.proto
首先实现设置proto文件的值,并将proto写入二进制文件和txt文件中。
实现代码如write_data.cpp所示:
#include <iostream>
#include <fstream>
#include <string>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdio>
#include <fstream>
#include <string>
#include <vector>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <glog/logging.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <fcntl.h>
#include <unistd.h>
const int kProtoReadBytesLimit = INT_MAX;
using google::protobuf::io::FileInputStream;
using google::protobuf::io::FileOutputStream;
using google::protobuf::io::ZeroCopyInputStream;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::ZeroCopyOutputStream;
using google::protobuf::io::CodedOutputStream;
using google::protobuf::Message;
/*
* John Doe
* 当 cin 读取到 John 和 Doe 之间的空格时,它就会停止阅读,只存储 John 作为 name 的值。在第二个输入语句中, cin 使用键盘缓冲区中找到的剩余字符,并存储 Doe 作为 city 的值。
* getline 的 C++ 函数。此函数可读取整行,包括前导和嵌入的空格,并将其存储在字符串对象中。
* getline 函数:getline(cin, inputLine);
* 其中 cin 是正在读取的输入流,而 inputLine 是接收输入字符串的 string 变量的名称。
*/
using namespace std;
#include "addressbook.pb.cc"
// 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 sex: ";
string sex;
getline(cin,sex);
if (sex == "man"){
person->set_sex(tutorial::Person::MAN);
} else {
person->set_sex(tutorial::Person::WOMAN);
}
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}
cout << "Enter a phone type:";
string type;
getline(cin,type);
if (type == "mobile") {
person->set_type(tutorial::Person::MOBILE);
} else if (type == "home") {
person->set_type(tutorial::Person::HOME);
} else if (type == "work") {
person->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
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;
}
cout << "Enter sex: ";
string sex;
getline(cin,sex);
if (sex == "man"){
phone_number->set_sex(tutorial::Person::MAN);
} else {
phone_number->set_sex(tutorial::Person::WOMAN);
}
}
}
void WriteProtoToTextFile(const Message& proto, string filename) {
//0644表示文件的权限
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
FileOutputStream* output = new FileOutputStream(fd);
CHECK(google::protobuf::TextFormat::Print(proto, output));
delete output;
close(fd);
}
// 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() {
// 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;
//}
string file = "txt.prototxt";
string txtfile = "proto.txt";
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input("txt.prototxt", ios::in | ios::binary);
if (!input) {
cout << file << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// Add an address.
while (true) {
cout << "Record or Exit (blank to exit) ";
string answer;
getline(cin, answer);
if (answer.empty()) {
break;
}
PromptForAddress(address_book.add_people());//更新proto
}
{
// Write the new address book back to disk.
fstream output("txt.prototxt", ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) { //写入二进制文件
cerr << "Failed to write address book." << endl;
return -1;
}
WriteProtoToTextFile(address_book,txtfile);//写入txt文件
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
最后将proto 内的内容写入了txt文件和二进制文件。
生成结果如下
打开proto.txt文件,内容如下:
people {
name: "jhon"
id: 1
phones {
sex: MAN
type: HOME
number: "545421"
}
phones {
sex: MAN
type: HOME
number: "56455445"
}
phones {
sex: WOMAN
type: HOME
number: "1245445"
}
email: "454654545"
sex: MAN
}
people {
name: "xiaoming"
id: 2125
phones {
sex: MAN
type: WORK
number: "454556"
}
email: "22425"
sex: MAN
type: HOME
}
people {
name: "xiaoxiao"
id: 5445
phones {
sex: MAN
type: HOME
number: "122345645"
}
phones {
sex: MAN
type: WORK
number: "4545454"
}
email: "asdsdfsa"
sex: MAN
type: HOME
}
people {
name: "gege"
id: 646
phones {
sex: MAN
number: "122455"
}
email: "45a4142"
sex: MAN
type: HOME
}
采用read_data.cpp代码将txt文件和二进制的文件读取进proto
read_data.cpp 代码如下:
#include <iostream>
#include <fstream>
#include <string>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdio>
#include <vector>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <glog/logging.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
const int kProtoReadBytesLimit = INT_MAX;
using google::protobuf::io::FileInputStream;
using google::protobuf::io::FileOutputStream;
using google::protobuf::io::ZeroCopyInputStream;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::ZeroCopyOutputStream;
using google::protobuf::io::CodedOutputStream;
using google::protobuf::Message;
#include "addressbook.pb.cc"
using namespace std;
// 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.has_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;
}
cout << phone_number.number() << endl;
}
}
}
bool ReadProtoFromTextFile(const string filename, Message* proto) {
//以只读方式(O_RDONLY)打开filename文件
int fd = open(filename.c_str(), O_RDONLY);
//使用glog库中的CHECK_NE函数检查是否读取文件成功
CHECK_NE(fd, -1) << "File not found: " << filename;
//使用FileInputStream流读取文件 ZeroCopyInputStream will clear the stream
FileInputStream* input = new FileInputStream(fd);
//使用Text格式解析input输入,并将读取到的数据赋值到proto上
//Merge()函数不会覆盖原来的proto文件记录的数据,Parse()函数会直接覆盖
bool success = google::protobuf::TextFormat::Parse(input, proto);//
//释放input对象
delete input;
//关闭stream流
close(fd);
//返回是否成功
return success;
}
// Main function: Reads the entire address book from a file and prints all
// the information inside.
int main() {
// 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;
{
// Read the existing address book.
fstream input("txt.prototxt", ios::in | ios::binary);
if (!address_book_read.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
ListPeople(address_book_read);
string file = "proto.txt";
// tutorial::AddressBook address_book_write;
ReadProtoFromTextFile(file,&address_book_read);
for(tutorial::Person people : address_book_read.people()) {
std::cout<<"name:"<<people.name()<<" ID:"<<people.id() <<" sex:"<<
people.sex()<<" type:"<<people.type()<<" email:"<<people.email()<<"\n";
int i = 1;
for(tutorial::Person::PhoneNumber phone : people.phones()){
std::cout<<"phone "<< i <<":"<<"\n"<<"sex:"<<phone.sex()<<" phone type:"<<phone.type()<<" phone number:"<<phone.number()<<"\n";
i++;
}
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
输出结果如下:
Person ID: 1
Name: jhon
E-mail address: 454654545
Home phone #: 545421
Home phone #: 56455445
Home phone #: 1245445
Person ID: 2125
Name: xiaoming
E-mail address: 22425
Work phone #: 454556
Person ID: 5445
Name: xiaoxiao
E-mail address: asdsdfsa
Home phone #: 122345645
Work phone #: 4545454
Person ID: 646
Name: gege
E-mail address: 45a4142
Home phone #: 122455
name:jhon ID:1 sex:1 type:1 email:454654545
phone 1:
sex:1 phone type:1 phone number:545421
phone 2:
sex:1 phone type:1 phone number:56455445
phone 3:
sex:0 phone type:1 phone number:1245445
name:xiaoming ID:2125 sex:1 type:1 email:22425
phone 1:
sex:1 phone type:2 phone number:454556
name:xiaoxiao ID:5445 sex:1 type:1 email:asdsdfsa
phone 1:
sex:1 phone type:1 phone number:122345645
phone 2:
sex:1 phone type:2 phone number:4545454
name:gege ID:646 sex:1 type:1 email:45a4142
phone 1:
sex:1 phone type:1 phone number:122455
如果使用bool success = google::protobuf::TextFormat::Merge(input, proto),那么下面的txt.prototxt文件内的内容最后也不会被proto.txt文件覆盖掉。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。