C++项目——云备份-③-实用工具类设计与实现

文章目录

专栏导读

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

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

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法Linux

🌸项目Gitee链接:https://gitee.com/li-yuanjiu/cloud-backup

1.文件实用工具类的设计

不管是客户端还是服务端,文件的传输备份都涉及到文件的读写,包括数据管理信息的持久化也是如此,因此首先设计封装文件操作类,这个类封装完毕之后,则在任意模块中对文件进行操作时都将变的简单化。

文件实用工具类FileUtil主要包含以下成员:

cpp 复制代码
class FileUtil
{
public:
    FileUtil(const std::string &filename) :_filename(filename);
    int64_t FileSize(); // 获取文件大小
    time_t LastMtime(); // 获取文件最后一次修改时间
    time_t LastATime(); // 获取文件最后一次访问时间
    std::string FileName(); // 获取文件路径中的纯文件名称 a/b/c/test.cc --> test.cc
    bool GetPosLen(std::string *body, size_t pos, size_t len); // 获取文件指定区间的数据
    bool GetContent(std::string *body); // 获取文件内容
    bool SetContent(const std::string &body); // 向文件写入数据
    bool Compress(const std::string &packname); // 压缩文件
    bool UnCompress(const std::string &filename); // 解压缩文件
    bool Exists(); // 判断文件是否存在
    bool Remove(); // 删除文件
    bool CreateDirectory(); // 创建文件目录
    bool ScanDirectory(std::vector<std::string> *array); // 浏览指定目录下的所有文件
private:
    std::string _filename; // 文件名--包含路径
};
  • _filename:文件名称(包含路径),例如 a/b/c/test.cc
  • FileUtil:构造函数;
  • FileSize:获取文件大小;
  • LastMtime:获取文件最后一次修改时间;
  • LastATime:获取文件最后一次访问时间;
  • FileName:获取文件路径中的纯文件名称 a/b/c/test.cc --> test.cc
  • GetPosLen:获取文件指定区间的数据;
  • GetContent:获取文件内容;
  • SetContent:向文件写入数据;
  • Compress:压缩文件;
  • UnCompress:解压缩文件;
  • Exists:判断文件是否存在;
  • Remove:删除文件;
  • CreateDirectory:创建文件目录;
  • ScanDirectory:浏览指定目录下的所有文件;

2.文件实用工具类的实现

2.1前置知识补充

FileUtil的实现中,我们使用了较多相对不常见库函数,在此先做复习。

2.1.1struct stat 与 stat介绍

认识stat函数之前我们首先认识一下struct stat类型。

在C语言中,struct stat是一个用于表示文件或文件系统对象属性的结构体类型。这个结构体通常用于与文件和目录相关的操作,例如获取文件的大小、访问权限、最后修改时间等信息。struct stat类型的定义通常由操作系统提供,因此其具体字段可能会因操作系统而异。

以下是一个典型的struct stat结构体的字段,尽管具体字段可能会因操作系统而异:

cpp 复制代码
struct stat {
    dev_t     st_dev;         // 文件所在设备的ID
    ino_t     st_ino;         // 文件的inode号
    mode_t    st_mode;        // 文件的访问权限和类型
    nlink_t   st_nlink;       // 文件的硬链接数量
    uid_t     st_uid;         // 文件的所有者的用户ID
    gid_t     st_gid;         // 文件的所有者的组ID
    off_t     st_size;        // 文件的大小(以字节为单位)
    time_t    st_atime;       // 文件的最后访问时间
    time_t    st_mtime;       // 文件的最后修改时间
    time_t    st_ctime;       // 文件的最后状态改变时间
    blksize_t st_blksize;     // 文件系统I/O操作的最佳块大小
    blkcnt_t  st_blocks;      // 文件占用的块数
};

struct stat结构体中的这些字段提供了关于文件或目录的各种信息。不同的操作系统可能会提供额外的字段,或者字段的意义可能会有所不同。

stat函数用于获取与指定路径名相关联的文件或目录的属性,并将这些属性填充到一个struct stat结构体中。以下是stat函数的函数原型:

cpp 复制代码
int stat(const char *pathname, struct stat *statbuf);
  • pathname是要获取属性的文件或目录的路径名;
  • statbuf是一个指向struct stat结构体的指针,用于存储获取到的属性信息;
  • stat函数返回一个整数值,如果操作成功,返回0;如果出现错误,返回-1,并设置errno全局变量以指示错误的类型。

注意需要包含头文件<sys/stat.h><unistd.h>来使用stat函数。

2.1.2std::experimental::filesystem认识

std::experimental::filesystem 库是 C++ 标准库的一部分,最早出现在 C++17 中,并被视为实验性的文件系统库。它提供了一组类和函数,用于处理文件系统操作,如文件和目录的创建、访问、遍历、复制、删除等。这个库的目的是使文件系统操作更加便捷,同时具有跨平台性,因此你可以在不同操作系统上执行相同的文件操作,而不需要考虑底层细节。

以下是一些 std::experimental::filesystem 库的主要特性和功能:

  • std::experimental::filesystem::path 类:用于表示文件路径。它可以自动处理不同操作系统的路径分隔符,使得代码更具可移植性。

  • 文件和目录操作:这个库提供了许多函数来执行常见的文件和目录操作,包括文件创建、复制、移动、删除,以及目录的创建、删除、遍历等。

  • 文件属性查询:你可以使用这个库来查询文件和目录的属性,如文件大小、修改时间等。

  • 异常处理std::experimental::filesystem 库定义了一些异常类,以处理与文件系统操作相关的错误,如文件不存在或无法访问等问题。

  • 迭代器:你可以使用迭代器来遍历目录中的文件和子目录,这是一个非常方便的功能,用于递归遍历文件系统。

需要注意的是,尽管 std::experimental::filesystem 在 C++17 中引入,但它是一个实验性的特性,并且不一定在所有编译器和平台上都得到完全支持。因此,一些编译器可能需要特定的编译选项或配置才能使用这个库。

从 C++17 开始,文件系统库已正式成为 C++ 标准的一部分,并迁移到 std::filesystem 命名空间中,而不再是实验性的特性。因此,在新的 C++标准中,建议使用 std::filesystem 库来执行文件系统操作。

2.2FileUtil实现

cpp 复制代码
namespace fs = std::experimental::filesystem;
class FileUtil
{
public:
    FileUtil(const std::string &filename) :_filename(filename)
    {}
    int64_t FileSize()
    {
        struct stat st;
        if(stat(_filename.c_str(), &st) < 0)
        {
            std::cout << "get file size failed!" << std::endl;
            return -1;
        }
        return st.st_size;
    }
    time_t LastMtime()
    {
        struct stat st;
        if(stat(_filename.c_str(), &st) < 0)
        {
            std::cout << "get last modify time failed!" << std::endl;
            return -1;
        }
        return st.st_mtime;
    }
    time_t LastATime()
    {
        struct stat st;
        if(stat(_filename.c_str(), &st) < 0)
        {
            std::cout << "get last access time failed!" << std::endl;
            return -1;
        }
        return st.st_atime;  
    }
    std::string FileName()
    {
        size_t pos = _filename.find_last_of("/");
        if(pos == std::string::npos)
        {
            return _filename;
        }
        return _filename.substr(pos+1);
    }
    bool GetPosLen(std::string *body, size_t pos, size_t len)
    {
        size_t fsize = FileSize();
        if(pos + len > fsize)
        {
            std::cout << "GetPosLen: get file len error" << std::endl;
            return false;
        }
        std::ifstream ifs;
        ifs.open(_filename, std::ios::binary);
        if(ifs.is_open() == false)
        {
            std::cout << "GetPosLen: open file failed!" << std::endl;
            return false;
        }
        ifs.seekg(pos, std::ios::beg); // 将文件指针定位到pos处
        body->resize(len);
        ifs.read(&(*body)[0], len);
        if(ifs.good() == false)
        {
            std::cout << "GetPosLen: get file content failed" << std::endl;
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }
    bool GetContent(std::string *body)
    {
        size_t fsize = FileSize();
        return GetPosLen(body, 0, fsize);
    }
    bool SetContent(const std::string &body)
    {
        std::ofstream ofs;
        ofs.open(_filename, std::ios::binary);
        if(ofs.is_open() == false)
        {
            std::cout << "SetContent: write open file failed" << std::endl;
            return false;
        }
        ofs.write(&body[0], body.size());
        if(ofs.good() == false)
        {
            std::cout << "SetContent: write open file failed" << std::endl;
            ofs.close();
            return false;
        }
        ofs.close();
        return true;
    }
    bool Compress(const std::string &packname)
    {
        // 1.获取源文件数据
        std::string body;
        if(GetContent(&body) == false)
        {
            std::cout << "compress get file content failed" << std::endl;
            return false;
        }
        // 2.对数据进行压缩
        std::string packed = bundle::pack(bundle::LZIP, body);
        // 3.将压缩的数据存储到压缩包文件中
        FileUtil fu(packname);
        if(fu.SetContent(packed) == false)
        {
            std::cout << "compress write packed data failed!" << std::endl;
            return false;
        }
        return true;
    }
    bool UnCompress(const std::string &filename)
    {
        // 1.将当前压缩包数据读取出来
        std::string body;
        if(GetContent(&body) == false)
        {
            std::cout << "Uncompress get file content failed!" << std::endl;
            return false;
        }
        // 2.对压缩的数据进行解压缩
        std::string unpacked = bundle::unpack(body);
        // 3.将解压缩的数据写入到新文件中
        FileUtil fu(filename);
        if(fu.SetContent(unpacked) == false)
        {
            std::cout << "Uncompress write packed data failed!" << std::endl;
            return false;
        }
        return true;
    }
    bool Exists()
    {
        return fs::exists(_filename);
    }
    bool Remove()
    {
        if(Exists() == false)
        {
            return true;
        }
        remove(_filename.c_str());
        return true;
    }
    bool CreateDirectory()
    {
        if(Exists()) return true;
        return fs::create_directories(_filename);
    }
    bool ScanDirectory(std::vector<std::string> *array)
    {
        for(auto& p : fs::directory_iterator(_filename)) // 迭代器遍历指定目录下的文件
        {
            if(fs::is_directory(p) == true) continue;
            // relative_path 带有路径的文件名
            array->push_back(fs::path(p).relative_path().string());
        }
        return true;
    }
private:
    std::string _filename;
};

3.JSON实用工具类的设计

Jsoncpp已经为我们你提供了序列化与反序列化接口,但是为了使得实用更加便捷,我们可以自己再封装一个JsonUtil的类。

JsonUtil类中包含以下成员:

cpp 复制代码
class JsonUtil
{
public:
    static bool Serialize(const Json::Value &root, std::string *str); // 序列化操作
    static bool Unserialize(const std::string &str, Json::Value *root); // 反序列化操作
};

由于前面的章节已经介绍过Json的使用,接下来我们直接看函数的实现。

4.JSON实用工具类的实现

cpp 复制代码
class JsonUtil
{
public:
    static bool Serialize(const Json::Value &root, std::string *str)
    {
        Json::StreamWriterBuilder swb;
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        std::stringstream ss;
        if(sw->write(root, &ss) != 0)
        {
            std::cout << "json write failed!" << std::endl;
            return false;
        }
        *str = ss.str();
        return true;
    }
    static bool Unserialize(const std::string &str, 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" << std::endl;
            return false; 
        }
        return true;
    }
};

5.实用工具类整理

最后,我们将两个实用工具类都放到util.hpp的头文件中。

cpp 复制代码
// util.hpp
#ifndef __MY_UTIL__
#define __MY_UTIL__
/*
    1.获取文件大小
    2.获取文件最后一次修改时间
    3.获取文件最后一次访问时间
    4.获取文件路径名中的文件名称 /abc/test.txt -> test.txt
    5.向文件写入数据
    6.获取文件数据
    7.获取文件指定位置 指定数据长度
    8.判断文件是否存在
    9.创建目录
    10.浏览获取目录下的所有文件路径名
    11.压缩文件
    12.解压缩所有文件
    13.删除文件
*/
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>
#include "bundle.h"

namespace cloud
{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    public:
        FileUtil(const std::string &filename) :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file size failed!" << std::endl;
                return -1;
            }
            return st.st_size;
        }
        time_t LastMtime()
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get last modify time failed!" << std::endl;
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get last access time failed!" << std::endl;
                return -1;
            }
            return st.st_atime;  
        }
        std::string FileName()
        {
            size_t pos = _filename.find_last_of("/");
            if(pos == std::string::npos)
            {
                return _filename;
            }
            return _filename.substr(pos+1);
        }
        bool GetPosLen(std::string *body, size_t pos, size_t len)
        {
            size_t fsize = FileSize();
            if(pos + len > fsize)
            {
                std::cout << "GetPosLen: get file len error" << std::endl;
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename, std::ios::binary);
            if(ifs.is_open() == false)
            {
                std::cout << "GetPosLen: open file failed!" << std::endl;
                return false;
            }
            ifs.seekg(pos, std::ios::beg); // 将文件指针定位到pos处
            body->resize(len);
            ifs.read(&(*body)[0], len);
            if(ifs.good() == false)
            {
                std::cout << "GetPosLen: get file content failed" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        bool GetContent(std::string *body)
        {
            size_t fsize = FileSize();
            return GetPosLen(body, 0, fsize);
        }
        bool SetContent(const std::string &body)
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            if(ofs.is_open() == false)
            {
                std::cout << "SetContent: write open file failed" << std::endl;
                return false;
            }
            ofs.write(&body[0], body.size());
            if(ofs.good() == false)
            {
                std::cout << "SetContent: write open file failed" << std::endl;
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            // 1.获取源文件数据
            std::string body;
            if(GetContent(&body) == false)
            {
                std::cout << "compress get file content failed" << std::endl;
                return false;
            }
            // 2.对数据进行压缩
            std::string packed = bundle::pack(bundle::LZIP, body);
            // 3.将压缩的数据存储到压缩包文件中
            FileUtil fu(packname);
            if(fu.SetContent(packed) == false)
            {
                std::cout << "compress write packed data failed!" << std::endl;
                return false;
            }
            return true;
        }
        bool UnCompress(const std::string &filename)
        {
            // 1.将当前压缩包数据读取出来
            std::string body;
            if(GetContent(&body) == false)
            {
                std::cout << "Uncompress get file content failed!" << std::endl;
                return false;
            }
            // 2.对压缩的数据进行解压缩
            std::string unpacked = bundle::unpack(body);
            // 3.将解压缩的数据写入到新文件中
            FileUtil fu(filename);
            if(fu.SetContent(unpacked) == false)
            {
                std::cout << "Uncompress write packed data failed!" << std::endl;
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool Remove()
        {
            if(Exists() == false)
            {
                return true;
            }
            remove(_filename.c_str());
            return true;
        }
        bool CreateDirectory()
        {
            if(Exists()) return true;
            return fs::create_directories(_filename);
        }
        bool ScanDirectory(std::vector<std::string> *array)
        {
            for(auto& p : fs::directory_iterator(_filename)) // 迭代器遍历指定目录下的文件
            {
                if(fs::is_directory(p) == true) continue;
                // relative_path 带有路径的文件名
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root, std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            if(sw->write(root, &ss) != 0)
            {
                std::cout << "json write failed!" << std::endl;
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool Unserialize(const std::string &str, 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" << std::endl;
                return false; 
            }
            return true;
        }
    };
}
#endif
相关推荐
唐诺4 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨5 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客6 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin6 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
yuanbenshidiaos7 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室8 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0018 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我588 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
fpcc8 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
呆萌很8 小时前
C++ 集合 list 使用
c++