C++编程中使用框架protobuf

protobuf作为protocol buffer协议使用最广泛的框架之一,在很多编程语言中都有对应实现,本篇文章介绍以下C++语言中如何使用protobuf。

目录

1.编译安装protobuf

2.Protobuf2和Protobuf3

3.定义proto接口文件

3.1Protobuf2定义

3.2Protobuf2定义

4.C++语言中使用protobuf

 4.1生成C++接口文件

4.2PB对象与字节流互转,PB对象与文件流

4.3CodedOutputStream与CodedInputStream使用 

4.4prototxt文本文件写和读

​编辑 4.5FileOutputStream和FileInputStream操作二进制文件 

 4.6PB对象与std::stringstream


1.编译安装protobuf

参考博客:

protocol buffer协议的两种常用框架protobuf和nanopb_nanopb repeated_hsy12342611的博客-CSDN博客

2.Protobuf2和Protobuf3

Protobuf有两个版本,Protobuf2和Protobuf3,目前使用最多是Protobuf3,关于Protobuf2和Protobuf3,有如下点需要说明

(1)在Protobuf2中,消息的字段可以加required和optional修饰符,也支持default修饰符指定默认值。默认一个optional字段如果没有设置,或者显式设置成了默认值,在序列化成二进制格式时,这个字段会被去掉,反序列化后,无法区分是当初没有设置还是设置成了默认值但序列化时被去掉了,虽然Protobuf2对于原始数据类型字段都有hasXxx()方法,在反序列化后,对于这个“缺失”字段,hasXxx()总是false,因此失去了其判定意义。

(2)在 Protobuf3中,去掉了required和optional修饰符,所有字段都是optional的,而且对于原始数据类型字段(包括repeated),不提供hasXxx()方法。

(3)缺失值和默认
缺失值或者显示设置默认值,效果是一样,序列化时字段会被去掉。
对于整数类型,默认值为0。
对于字符串,默认值为空字符串。
对于字节,默认值为空字节。
对于布尔值,默认值为false。
对于数字类型,默认值为零。
对于枚举,默认值为第一个定义的枚举值,必须为0。

3.定义proto接口文件

以表达人信息为例,对比Protobuf2和Protobuf3定义person.proto

3.1Protobuf2定义

syntax = "proto2";
package cunion.test;


// proto2
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];
    }
    optional repeated PhoneNumber phone = 4;
    message Addr {
        optional string province = 1;
        optional string city = 2;
        optional string county = 3;
    }
    optional Addr addr = 5;
}

3.2Protobuf2定义

syntax = "proto3";

package cunion.test;

// proto3

message Person {

string name = 1;

int32 id = 2;

string email = 3;

enum PhoneType {

MOBILE = 0;

HOME = 1;

WORK = 2;

}

message PhoneNumber {

string number = 1;

PhoneType type = 2;

}

repeated PhoneNumber phone = 4;

message Addr {

string province = 1;

string city = 2;

string county = 3;

}

Addr addr = 5;

}

4.C++语言中使用protobuf

本人安装是protobuf V3.6.1版本,所以就以protobuf3为例介绍C++中使用protobuf框架。

 4.1生成C++接口文件

/home/tiger/protobuf-3.6.1/protobuf/bin/protoc  -I=/home/tiger/cpp/protocalbuffer --cpp_out=/home/tiger/cpp/protocalbuffer  person.proto 

4.2PB对象与字节流互转,PB对象与文件流

#include <algorithm>
#include <iostream>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>  

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "person.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"  // ArrayOutputStream
#include "google/protobuf/io/zero_copy_stream_impl.h"   // FileOutputStream    OstreamOutputStream
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/delimited_message_util.h"

// ArrayOutputStream, FileOutputStream, OstreamOutputStream  
// ArrayInputStream, FileInputStream, OstreamInputStream均继承ZeroCopyOutputStream

using namespace ::cunion::test;
using namespace std;


// PB对象与字节流互转,PB对象与文件流
int use_1() {
    Person person1;
    person1.set_name("hello tree");
    //person1.has_phone(); //error
    std::cout << person1.has_addr() << std::endl;
    std::cout << "person1 " << person1.DebugString() << std::endl;

    std::string strPerson1;
    //PB对象转字节流
    person1.SerializeToString(&strPerson1);  // person1.SerializeToArray(buffer, n);
    Person person2;
    //字节流转PB对象
    person2.ParseFromString(strPerson1);
    std::cout << person2.has_addr() << std::endl;
    std::cout << "person2 " << person2.DebugString() << std::endl;

    const char* fileName = "person.bin";
    // 写文件
    std::fstream output(fileName, ios::out | ios::binary);
    if (!output)
    {
        cout<< "output" << fileName << " error"<<endl;
    }  
    //PB对象转文件流
    person1.SerializeToOstream(&output);
    output.close();

    // 读文件
    fstream input(fileName, ios::in | ios::binary);
    if (!input)
    {
        cout<<"input "<< fileName << " error"<<endl;
        return -1;
    }
    Person person3;
    //文件流转PB对象
    if (!person3.ParseFromIstream(&input))
    {
        cerr << "person3.ParseFromIstream !"<< endl;
    }
    std::cout << "person3 " << person2.DebugString() << std::endl;
}


int main() {

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    use_1();

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}

编译:

//错误编译方法1
g++  person.pb.cc  main.cpp  -I /home/tiger/protobuf-3.6.1/protobuf/include -lpthread  -L /home/tiger/protobuf-3.6.1/protobuf/lib  -lprotobuf  


//错误编译方法2
g++   -I /home/tiger/protobuf-3.6.1/protobuf/include -L /home/tiger/protobuf-3.6.1/protobuf/lib  -lpthread -lprotobuf person.pb.cc  main.cpp  

// 正确编译方法
g++ -I /home/tiger/protobuf-3.6.1/protobuf/include person.pb.cc  main.cpp  -L /home/tiger/protobuf-3.6.1/protobuf/lib  -lprotobuf -lpthread


运行:

export LD_LIBRARY_PATH=/home/tiger/protobuf-3.6.1/protobuf/lib
./a.out

运行结果如下:

4.3CodedOutputStream与CodedInputStream使用 

#include <algorithm>
#include <iostream>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>  

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "person.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"  // ArrayOutputStream
#include "google/protobuf/io/zero_copy_stream_impl.h"   // FileOutputStream    OstreamOutputStream
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/delimited_message_util.h"

// ArrayOutputStream, FileOutputStream, OstreamOutputStream  
// ArrayInputStream, FileInputStream, OstreamInputStream均继承ZeroCopyOutputStream

using namespace ::cunion::test;
using namespace std;


// CodedOutputStream与CodedInputStream使用
int use_2() {
    Person person;
    person.set_name("hello liudehua");

    int len = person.ByteSize();

    char *buffer = new char[len];
	memset(buffer, 0, len);
    std::shared_ptr<char> pBuf;
    pBuf.reset(buffer);

    //写入pBuf  
    google::protobuf::io::ArrayOutputStream arrayOut(pBuf.get(), len);
    google::protobuf::io::CodedOutputStream codedOut(&arrayOut);
    codedOut.WriteVarint32(len);
    person.SerializeToCodedStream(&codedOut);
    std::cout << "person " << person.DebugString() << std::endl;

    //读取pBuf
    Person person1;
	google::protobuf::io::ArrayInputStream array_in(pBuf.get(), len);
	google::protobuf::io::CodedInputStream coded_in(&array_in);
	google::protobuf::uint32 size;
	coded_in.ReadVarint32(&size);
	google::protobuf::io::CodedInputStream::Limit msg_limit = coded_in.PushLimit(size);
	person1.ParseFromCodedStream(&coded_in);
	coded_in.PopLimit(msg_limit);
    std::cout << "person1 " << person1.DebugString() << std::endl;
}

int main() {

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    use_2();

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}

编译运行:

 g++ -I /home/tiger/protobuf-3.6.1/protobuf/include person.pb.cc  main.cpp  -L /home/tiger/protobuf-3.6.1/protobuf/lib  -lprotobuf -lpthread
./a.out 
运行结果如下:

通过运行结果看:

google::protobuf::io::ArrayOutputStream和google::protobuf::io::CodedOutputStream实现PB对象的序列化,google::protobuf::io::ArrayInputStream和google::protobuf::io::CodedInputStream实现反序列化。

4.4prototxt文本文件写和读

#include <algorithm>
#include <iostream>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>  

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "person.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"  // ArrayOutputStream
#include "google/protobuf/io/zero_copy_stream_impl.h"   // FileOutputStream    OstreamOutputStream
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/delimited_message_util.h"

// ArrayOutputStream, FileOutputStream, OstreamOutputStream  
// ArrayInputStream, FileInputStream, OstreamInputStream均继承ZeroCopyOutputStream

using namespace ::cunion::test;
using namespace std;

// FileOutputStream FileInputStream 
// prototxt文本文件
int use_3() {
    const char* fileName = "person1.prototxt";

    // 写入
    int fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0777);
    google::protobuf::io::FileOutputStream * output = new google::protobuf::io::FileOutputStream(fd);
 
    Person person;
    person.set_name("hello guofucheng");
    person.set_id(110);
    google::protobuf::TextFormat::Print(person, output);
    delete output;
    close(fd);
    std::cout << "person " << person.DebugString() << std::endl;

    // 读取
    fd = open(fileName, O_RDONLY);
    Person person1;
    google::protobuf::io::FileInputStream * input = new google::protobuf::io::FileInputStream(fd);
    bool success = google::protobuf::TextFormat::Parse(input, &person1);
    delete input;
    close(fd);
    std::cout << "person1 " << person1.DebugString() << std::endl;

}

int main() {

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    use_3();

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}

运行结果如下:

 4.5FileOutputStream和FileInputStream操作二进制文件 

#include <algorithm>
#include <iostream>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>  
 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
#include "person.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"  // ArrayOutputStream
#include "google/protobuf/io/zero_copy_stream_impl.h"   // FileOutputStream    OstreamOutputStream
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/delimited_message_util.h"
 
// ArrayOutputStream, FileOutputStream, OstreamOutputStream  
// ArrayInputStream, FileInputStream, OstreamInputStream均继承ZeroCopyOutputStream
 
using namespace ::cunion::test;
using namespace std;
 
// FileOutputStream和FileInputStream操作二进制文件
int use_4() {
    const char* fileName = "person3.bin";
 
    // 写入
    int fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0777);
    
    google::protobuf::io::ZeroCopyOutputStream* raw_output = new google::protobuf::io::FileOutputStream(fd);
    google::protobuf::io::CodedOutputStream* coded_output = new google::protobuf::io::CodedOutputStream(raw_output);
 
    Person person;
    person.set_name("hello zhangxueyou");
    person.set_id(119);
    coded_output->WriteVarint32(person.ByteSize());
    person.SerializeToCodedStream(coded_output);
 
    Person person1;
    person1.set_name("hello liming");
    person1.set_id(120);
    coded_output->WriteVarint32(person1.ByteSize());
    person1.SerializeToCodedStream(coded_output);
 
    person1.set_name("hello wobuzhidao");
    person1.set_id(121);
    coded_output->WriteVarint32(person1.ByteSize());
    person1.SerializeToCodedStream(coded_output);
 
    person1.set_name("hello nibuzhidao");
    person1.set_id(122);
    coded_output->WriteVarint32(person1.ByteSize());
    person1.SerializeToCodedStream(coded_output);
 
    delete coded_output;
    delete raw_output;
    close(fd);
 
    std::cout << "person " << person.DebugString() << std::endl;
    std::cout << "person1 " << person1.DebugString() << std::endl;
 
    // 读取
    fd = open(fileName, O_RDONLY);
    google::protobuf::io::FileInputStream fin(fd);
    Person person2;
    
    google::protobuf::io::FileInputStream input(fd);
    google::protobuf::io::CodedInputStream codedIn(&input);
 
    google::protobuf::uint32 size;
    while (true) {
        if (!codedIn.ReadVarint32(&size)) {
            return -1;
        }
        std::cout << "person2 " << size << std::endl;
	    google::protobuf::io::CodedInputStream::Limit msg_limit = codedIn.PushLimit(size);
	    person2.ParseFromCodedStream(&codedIn);
	    codedIn.PopLimit(msg_limit);
        std::cout << "person2 " << person2.DebugString() << std::endl;
    }
}
 
int main() {
 
    GOOGLE_PROTOBUF_VERIFY_VERSION;
 
    use_4();
 
    google::protobuf::ShutdownProtobufLibrary();
 
    return 0;
}

编译运行:

g++ -I /home/tiger/protobuf-3.6.1/protobuf/include person.pb.cc  main.cpp  -L /home/tiger/protobuf-3.6.1/protobuf/lib  -lprotobuf -lpthread

export LD_LIBRARY_PATH=/home/tiger/protobuf-3.6.1/protobuf/lib

./a.out 

运行结果如下:

 4.6PB对象与std::stringstream

#include <algorithm>
#include <iostream>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>  

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "person.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"  // ArrayOutputStream
#include "google/protobuf/io/zero_copy_stream_impl.h"   // FileOutputStream    OstreamOutputStream
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/delimited_message_util.h"

// ArrayOutputStream, FileOutputStream, OstreamOutputStream  
// ArrayInputStream, FileInputStream, OstreamInputStream均继承ZeroCopyOutputStream

using namespace ::cunion::test;
using namespace std;

// std::stringstream
int use_5() {
    std::stringstream stream;

    Person person;
    
    {
        person.set_name("name one");
        person.set_id(1);
        google::protobuf::util::SerializeDelimitedToOstream(person, &stream);
    }

    {
        person.set_name("name two");
        person.set_id(2);
        google::protobuf::util::SerializeDelimitedToOstream(person, &stream);
    }

    {
        person.set_name("name three");
        person.set_id(3);
        google::protobuf::util::SerializeDelimitedToOstream(person, &stream);
    }

    {
        person.set_name("name four");
        person.set_id(4);
        google::protobuf::util::SerializeDelimitedToOstream(person, &stream);
    }


    bool keep = true;
    bool clean_eof = true;
    Person person1;
    google::protobuf::io::IstreamInputStream zstream(&stream);
    while (keep)
    {
        clean_eof = true;
        keep = google::protobuf::util::ParseDelimitedFromZeroCopyStream(&person1, &zstream, &clean_eof);
        std::cout << person1.name() << " " << person1.id() << std::endl;
    }
    
    return 0;
}


int main() {

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    use_5();

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}

运行结果如下:

从上面的举例可以看出protobuf框架对C++提供了丰富的操作API,利用这些API可以很容易处理java,python等语言处理过后的PB文件流。 

猜你喜欢

转载自blog.csdn.net/hsy12342611/article/details/129914975