如果您已經初步了解了Protocol Buffers并打算試著使用它,本文符合您的胃口。如果您剛聽說Protocol Buffers,請先到本文末尾的附錄區看一看。
1.下載并安裝Protocol Buffers
可以從官方下載源碼包,linux下和Solaris下的安裝直接見源碼包中的“README.txt”。這里詳細說下Windows下的安裝,源碼包里有一個“vsprojects”目錄,其中就是vs的工程文件和解決方案。用vs(版本得高點)打開“protobuf.sln”解決方案,編譯。其中包括四個工程 libprotobuf(接口dll庫)、libprotoc(轉換器的實現庫)、protoc(windows下轉換器的實現)、tests(使用gTest庫進行的測試)。編譯好之后在Debug目錄下可以找到“libprotobuf.dll、libprotobuf.lib”,這個是我們的程序要使用的動態鏈接庫和導入庫。“libprotoc.dll、libprotoc.lib”,這個是完成.proto文件到cpp、java、python格式數據轉換的庫。“protoc.exe”,這個是windows下轉換程序(它使用了剛才的libprotoc庫),這個程序的靜態鏈接版本也在此項目老家提供下載。
2.設置編譯環境
linux下,只要將Protocol Buffers源碼包安裝到系統即可開始使用。而windows下需要設置一下編譯環境,將“src”目錄加入到編譯器的頭文件搜索路徑,將“vsprojects\Debug”目錄加入到編譯器的lib搜索路徑中。為了更方便的在windows命令行下使用protoc.exe轉換程序,可以將“vsprojects\Debug”目錄添加到系統PATH變量中。
3.編寫.proto數據描述文件
這里仿照源碼中例子,寫出“addressbook.proto”文件。內容如下:
--code begin--
package tutorial;
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person = 1;
}
--code end--
4.使用protoc(windows下是protoc.exe)生成c++頭文件及類文件。
protoc.exe --cpp_out=./ addressbook.proto
如果沒有錯誤,程序將沒有任何輸出。并且當前目錄下多出兩個文件“addressbook.pb.h”和“addressbook.pb.cc”。
5.編寫C++程序使用它們
新建vs工程,除了設置以上的頭文件搜索路徑和庫文件搜索路徑外,還要鏈接到庫“libprotobuf.lib”。將4步生成的一個.h文件和一個.cpp文件添加并拷貝到工程里,由于vs的特性(需要預編譯頭),所以在addressbook.pb.cc開頭添加“#include "stdafx.h"”,主代碼如下,然后編譯。這個演示程序需要一個參數用于指定數據文件文件名,第一次運行,會生成這個數據文件。它會先讓用戶輸入一條通訊錄信息并添加進數據文件,然后再顯示出指定的數據文件中所有的數據。注意,為了使DEMO程序可以運行,別忘了拷貝“vsprojects\Debug”目錄下的動態鏈接庫“libprotobuf.dll”到當前目錄。
--code begin--
// testprotocolbuffer.cpp : 定義控制臺應用程序的入口點。
//
#include "stdafx.h"
// See README.txt for information and build instructions.
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
// 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_phone();
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;
}
}
}
// 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.person_size(); i++) {
const tutorial::Person& person = address_book.person(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.phone_size(); j++) {
const tutorial::Person::PhoneNumber& phone_number = person.phone(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;
}
}
}
// 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 << "使用方法: " << argv[0] << " 想要生成的存儲數據的文件" << 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] << ": 指定的文件沒找到,創建一個新文件." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "解析addressbook數據文件失敗。" << endl;
return -1;
}
}
// Add an address.
PromptForAddress(address_book.add_person());
{
// Write the new address book back to disk.
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "寫入文件失敗。" << endl;
return -1;
}
}
//再從文件中讀取剛才那個數據
tutorial::AddressBook address_book2;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!address_book2.ParseFromIstream(&input)) {
cerr << "解析文件失敗。" << endl;
return -1;
}
}
ListPeople(address_book2);
return 0;
}
--code end--
運行方式和結果如下圖:

備注:寫這東西好累啊。。。
=====================附錄====================
1.Protocol Buffers是Google自己的一種數據交換格式。其簡介可以參考文章“谷歌發布內部數據語言 比XML快近100倍”。
2.如果想要了解Protocol Buffers相關的詳細信息,請訪問它的老家“Protocol Buffers”。