C++ protobuf 将从txt读取到的数据赋值到proto上方法

假如我们有一个问题是关于:存储一个人的名字(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文件和二进制文件。
生成结果如下
C++ protobuf 将从txt读取到的数据赋值到proto上方法
打开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文件覆盖掉。

上一篇:ProtoBuf 对象转换


下一篇:google protocol buffer——protobuf的问题及改进一