• C++项目——云备份-②-第三方库认识


    专栏导读

    🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

    🌸专栏简介:本文收录于 C++项目——云备份

    🌸相关专栏推荐:C语言初阶系列C语言进阶系列 C++系列数据结构与算法Linux
    🌸项目Gitee链接:https://gitee.com/li-yuanjiu/cloud-backup

    在这里插入图片描述

    1. json 认识

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用于在不同应用程序之间传递和存储数据。它的设计目标是易于阅读和编写,同时也易于解析和生成。JSON采用文本格式,通常以.json文件扩展名存储,以及一种键-值对的结构,其中数据以一种易于理解的方式表示,适合机器和人类阅读。

    1.1 JSON 数据结构的特点

    • 键-值对:JSON 数据由键-值对组成,键和值之间使用冒号分隔,键-值对之间使用逗号分隔。键通常是字符串,值可以是字符串数字布尔值数组对象null等。
    {
        "name": "小明",
        "age": 30,
        "isStudent": true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 对象:对象是一种包含键-值对的数据结构,用大括号{}表示。对象的键是唯一的
    {
        "person": {
            "name": "Alice",
            "age": 25
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 数组:数组是一种有序的值的集合,用方括号[]表示。数组可以包含各种数据类型,包括对象和其他数组。
    {
        "fruits": ["apple", "banana", "orange"]
    }
    
    • 1
    • 2
    • 3
    • 字符串:字符串是以双引号""括起来的文本。字符串可以包含任何字符,包括特殊字符和转义序列。
    {
        "message": "Hello, World!"
    }
    
    • 1
    • 2
    • 3
    • 数字:数字可以是整数或浮点数,不需要引号包围。
    {
        "price": 19.99
    }
    
    • 1
    • 2
    • 3
    • 布尔值:表示真或假的值,可以是truefalse
    {
        "isSunny": true
    }
    
    • 1
    • 2
    • 3
    • null:表示空值或缺失值。
    {
        "data": null
    }
    
    • 1
    • 2
    • 3

    2. jsoncpp库认识

    jsoncpp 库用于实现 json 格式的序列化和反序列化,完成将多个数据对象组织成为 json 格式字符串,以及将 json 格式字符串解析得到多个数据对象的功能。

    这其中主要借助三个类以及其对应的少量成员函数完成:

    json数据对象类

    //Json数据对象类
    class Json::Value{
      Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取数据都可以通过
      Value& operator[](const std::string& key);//简单的方式完成 val["姓名"] = "小明";
      Value& operator[](const char* key);
      Value removeMember(const char* key);//移除元素
      const Value& operator[](ArrayIndex index) const; //val["成绩"][0]
      Value& append(const Value& value);//添加数组元素val["成绩"].append(88);
      ArrayIndex size() const;//获取数组元素个数 val["成绩"].size();
      std::string asString() const;//转string string name = val["name"].asString();
      const char* asCString() const;//转char*  char *name = val["name"].asCString();
      Int asInt() const;//转int int age = val["age"].asInt();
      float asFloat() const;//转float
      bool asBool() const;//转 bool
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    json序列化类

    //json序列化类,低版本用这个更简单
    class JSON_API Writer {
     virtual std::string write(const Value& root) = 0;
    }
    class JSON_API FastWriter : public Writer {
    virtual std::string write(const Value& root);
    }
    class JSON_API StyledWriter : public Writer {
     virtual std::string write(const Value& root);
    }
    //json序列化类,高版本推荐,如果用低版本的接口可能会有警告
    class JSON_API StreamWriter {
      virtual int write(Value const& root, std::ostream* sout) = 0;
    }
    class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
      virtual StreamWriter* newStreamWriter() const;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    json反序列化类

    //json反序列化类,低版本用起来更简单
    class JSON_API Reader {
    bool parse(const std::string& document, Value& root, bool collectComments = true);
    }
    //json反序列化类,高版本更推荐
    class JSON_API CharReader {
      virtual bool parse(char const* beginDoc, char const* endDoc,
               Value* root, std::string* errs) = 0;
    }
    class JSON_API CharReaderBuilder : public CharReader::Factory {
      virtual CharReader* newCharReader() const;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3. json实现序列化案例

    /*
        json 序列化
    */
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        const char* name = "小明";
        int age  = 18;
        float score[] = {77.5, 88, 93.6}; // 语数英成绩
    
        Json::Value root; // 定义一个Value对象
        root["name"] = name;
        root["age"] = age;
        root["成绩"].append(score[0]); // 在数组中插入数据用append函数
        root["成绩"].append(score[1]);
        root["成绩"].append(score[2]);
        
        // std::cout << root << std::endl;
        Json::StreamWriterBuilder swb;
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        std::stringstream ss;
        sw->write(root, &ss);
        std::cout << ss.str() << std::endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    $ g++ -o json_example json_example.cpp -ljsoncpp
    $ ./json_example
    
    • 1
    • 2

    运行结果
    在这里插入图片描述

    4. json实现反序列化案例

    /*
        json 反序列化
    */
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        std::string str = R"({"姓名":"小黑", "年龄":19, "成绩":[58.5, 44, 20]})";
        Json::Value root;
        Json::CharReaderBuilder crb;
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
        std::string err;
        bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
        if(ret == false)
        {
            std::cout << "parse error: " << err << std::endl;
            return -1; 
        }
    
        std::cout << root["姓名"].asString() << std::endl;
        std::cout << root["年龄"].asInt() << std::endl;
        int sz = root["成绩"].size();
        for(int i = 0; i < sz; i++)
        {
            std::cout << root["成绩"][i] << std::endl; 
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    $ g++ -o json_example2 json_example2.cpp -ljsoncpp
    $ ./json_example2
    
    • 1
    • 2

    运行结果

    在这里插入图片描述

    5. bundle文件压缩库认识

    Bundle 是一个嵌入式压缩库,支持23种压缩算法和2种存档格式。使用的时候只需要加入两个文件 bundle.hbundle.cpp 即可。

    以下是常用接口:

    namespace bundle
    {
     // low level API (raw pointers)
     bool is_packed( *ptr, len );
     bool is_unpacked( *ptr, len );
     unsigned type_of( *ptr, len );
     size_t len( *ptr, len );
     size_t zlen( *ptr, len );
     const void *zptr( *ptr, len );
     bool pack( unsigned Q, *in, len, *out, &zlen );
     bool unpack( unsigned Q, *in, len, *out, &zlen );
     // medium level API, templates (in-place)
     bool is_packed( T );
     bool is_unpacked( T );
     unsigned type_of( T );
     size_t len( T );
     size_t zlen( T );
     const void *zptr( T );
     bool unpack( T &, T );
     bool pack( unsigned Q, T &, T );
     // high level API, templates (copy)
     T pack( unsigned Q, T );
     T unpack( T );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    6. bundle库实现文件压缩案例

    #include 
    #include 
    #include 
    #include "bundle.h"
    
    int main(int argc, char* argv[])
    {
        std::cout << "argv[1] 是原始文件路径名称\n";
        std::cout << "argv[2] 是压缩包名称\n";
        if(argc < 3) return -1;
    
        std::string ifilename = argv[1];
        std::string ofilename = argv[2];
    
        std::ifstream ifs;
        ifs.open(ifilename, std::ios::binary); // 打开原始文件
        ifs.seekg(0, std::ios::end); // 跳转读写位置到末尾
        size_t fsize = ifs.tellg(); // 获取末尾偏移量--文件长度
        ifs.seekg(0, std::ios::beg); // 跳转到文件起始
        std::string body;
        body.resize(fsize); // 调整body大小为文件大小
        ifs.read(&body[0], fsize); // 读取文件所有数据到body
    
        std::string packed = bundle::pack(bundle::LZIP, body); // 以lzip格式压缩文件数据
    
        std::ofstream ofs;
        ofs.open(ofilename, std::ios::binary); // 打开压缩包文件
        ofs.write(&packed[0], packed.size()); // 将压缩后的数据写入压缩包文件
    
        ifs.close();
        ofs.close();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    $ g++ -o compress compress.cpp bundle.cpp -lpthread
    $ ./compress bundle.cpp bundle.cpp.lz
    
    • 1
    • 2

    运行结果

    在这里插入图片描述

    7.bundle库实现文件解压缩案例

    #include 
    #include 
    #include 
    #include "bundle.h"
    
    int main(int argc, char* argv[])
    {
      if(argc < 3)
      {
        printf("argv[1]是压缩包名称\n");
        printf("argv[2]是解压缩后的文件\n");
        return -1;
      }
      std::string ifilename = argv[1]; // 压缩包名
      std::string ofilename = argv[2]; // 解压缩后文件名
    
      std::ifstream ifs;
      ifs.open(ifilename, std::ios::binary); // 打开压缩文件
      ifs.seekg(0, std::ios::end); // 跳转到读写位置到文件末尾
      size_t fsize = ifs.tellg(); // 获取末尾偏移量--获取文件长度
      ifs.seekg(0, std::ios::beg); // 返回到文件起始
      std::string body;
      body.resize(fsize); // 调整body大小为文件大小
      ifs.read(&body[0], fsize); // 读取压缩文件所有内容到body
      ifs.close();
    
      std::string unpacked = bundle::unpack(body); // 进行解压缩,将解压缩后的数据保存到unpack中
      std::ofstream ofs;
      ofs.open(ofilename, std::ios::binary); // 打开文件
      ofs.write(&unpacked[0], unpacked.size()); // 将解压缩后的数据写入文件
      ofs.close();
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    $ g++ -o uncompress uncompress.cpp bundle.cpp -lpthread
    $ ./uncompress bundle.cpp.lz bundle.tmp
    $ 计算两个文件的md5值,看是否完全相同
    $ md5 bundle.cpp
    $ md5sum bundle.tmp
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行结果

    在这里插入图片描述

    8.httplib库认识

    httplib 库,一个 C++11 单文件头的跨平台 HTTP/HTTPS 库。安装起来非常容易。只需包含 httplib.h 在你的代码中即可。

    httplib 库实际上是用于搭建一个简单的 http 服务器或者客户端的库,这种第三方网络库,可以让我们免去搭建服务器或客户端的时间,把更多的精力投入到具体的业务处理中,提高开发效率。

    常用接口如下:

    namespace httplib{
      struct MultipartFormData {
        std::string name;
        std::string content;
        std::string filename;
        std::string content_type;
     };
      using MultipartFormDataItems = std::vector<MultipartFormData>;
      struct Request {
        std::string method;
        std::string path;
        Headers headers;
        std::string body;
        // for server
        std::string version;
        Params params;
        MultipartFormDataMap files;
        Ranges ranges;
        bool has_header(const char *key) const;
        std::string get_header_value(const char *key, size_t id = 0) const;
        void set_header(const char *key, const char *val);
        bool has_file(const char *key) const;
        MultipartFormData get_file_value(const char *key) const;
     };
      struct Response {
        std::string version;
        int status = -1;
        std::string reason;
        Headers headers;
        std::string body;
        std::string location; // Redirect location
    	void set_header(const char *key, const char *val);
        void set_content(const std::string &s, const char *content_type);
     };
    class Server {
        using Handler = std::function<void(const Request &, Response &)>;
        using Handlers = std::vector<std::pair<std::regex, Handler>>;
        std::function<TaskQueue *(void)> new_task_queue;
        Server &Get(const std::string &pattern, Handler handler);
    Server &Post(const std::string &pattern, Handler handler);
        Server &Put(const std::string &pattern, Handler handler);
    Server &Patch(const std::string &pattern, Handler handler); 
    Server &Delete(const std::string &pattern, Handler handler);
    Server &Options(const std::string &pattern, Handler handler);
        bool listen(const char *host, int port, int socket_flags = 0);
    };
    class Client {
        Client(const std::string &host, int port);
    	Result Get(const char *path, const Headers &headers);
        Result Post(const char *path, const char *body, size_t content_length,
           const char *content_type);
        Result Post(const char *path, const MultipartFormDataItems &items);
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    9. httplib库搭建简单服务器案例

    #include 
    #include "httplib.h"
    using namespace httplib;
    void Hello(const Request &req, Response &rsp)
    {   
        rsp.set_content("Hello World!", "text/plain");
        rsp.status = 200; // 设置状态码
    }
    void Numbers(const Request &req, Response &rsp)
    {   
        auto num = req.matches[1]; // 0里边保存的是整体path,往后下标中保存的是捕捉的数据
        rsp.set_content(num, "text/plain");
        rsp.status = 200;
    }
    void Multipart(const Request &req, Response &rsp)
    {
        auto ret = req.has_file("file");
        if(ret == false)
        {
            std::cout << "not file upload\n";
            rsp.status = 400;
            return;
        }
        const auto& file = req.get_file_value("file");
        rsp.body.clear();
        rsp.body = file.filename; // 文件名称
        rsp.body += "\n";
        rsp.body += file.content; // 文件内容
        rsp.set_header("Content-Type", "text/plain");
        rsp.status = 200;
        return;
    }
    int main()
    {
        httplib::Server sever; // 实例化Sever对象用于搭建服务器
        sever.Get("/hi", Hello); // 注册一个针对/hi的Get请求的处理函数映射关系
        sever.Get(R"(/numbers/(\d+))", Numbers);
        sever.Post("/multipart", Multipart);
        sever.listen("0.0.0.0", 9090);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    $ g++ -o server httpserver.cpp -std=c++14 -lpthread
    $ ./server
    
    • 1
    • 2

    运行结果

    在这里插入图片描述

    注意:测试时一定要关闭防火墙,设置安全组开放端口

    10. httplib库搭建简单客户端案例

    #include "httplib.h"
    #define SERVER_IP "你的服务器IP地址"
    #define SERVER_PORT 9090
    
    int main()
    {
        httplib::Client client(SERVER_IP, SERVER_PORT); // 实例化client对象,用于搭建客户端
        // 以下仅为上传文件的测试,其他两个测试在浏览器上进行
        httplib::MultipartFormData item;
        item.name = "file"; // 该"file"匹配的就是服务器程序代码中的"file"
        item.filename = "hello.txt"; // 文件名
        item.content = "Hello World!"; // 文件内容
        item.content_type = "text/plain";
        
        httplib::MultipartFormDataItems items;
        items.push_back(item);
    
        auto res = client.Post("/multipart", items);
        std::cout << res->status << std::endl;
        std::cout << res->body << std::endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    $ g++ -o client httpclient.cpp -std=c++14 -lpthread
    $ ./client
    
    • 1
    • 2

    运行结果

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    Echarts-3D柱状图
    el-table 多表格弹窗嵌套数据显示异常错乱问题
    一个简单的CMake实例
    JavaScript对象:我们真的需要模拟类吗?
    Java开发学习(十四)----Spring整合Mybatis及Junit
    Java默认方法的使用
    一文看懂TikTok养号
    h264编码流程分析
    神经网络迭代次数的一个近似关系
    达人评测 锐龙r7 6800u和r5 6600h差距 r76800u和r56600h对比
  • 原文地址:https://blog.csdn.net/gllll_yu/article/details/133996704