项目实现:云备份②(文件操作、Json等工具类的实现)

news2024/9/21 23:11:53

云备份

  • 前言
  • 文件操作实用工具类设计
    • 文件属性的获取
    • 文件的读写操作
    • 文件压缩与解压缩的实现
    • 文件目录操作
  • Json 实用工具类设计
  • 编译优化

前言

如果有老铁不知道当前项目实现的功能是什么的话,可以先移步这篇文章内容: 云备份项目的介绍

其中介绍了云备份项目的基本功能、环境搭建的详情。

文件操作实用工具类设计

文件属性的获取

创建 src 目录,在 src 目录下进行 util.hpp 文件的代码编写,实现文件工具类:

  1. int64_t FileSize()获取文件大小
  2. time_t LastMTime()获取文件最后一次的修改时间
  3. time_t LastATime()获取文件最后一次的访问时间
  4. std::string FileName()获取文件名称
#include <iostream>
#include <string>
#include <vector>
#include <sys/stat.h>

namespace cloud
{
    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 filesize false\n";
                return -1;
            }
            return st.st_size;
        }
        // 获取文件最后一次的修改时间
        time_t LastMTime()
        {
            struct stat st; // 用于获取文件属性
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file last modify time false\n";
                return -1;
            }
            return st.st_mtime; // 文件的修改时间
        }
        // 获取文件最后一次的访问时间
        time_t LastATime()
        {
            struct stat st; // 用于获取文件属性
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file last access time false\n";
                return -1;
            }
            return st.st_atime; // 文件的修改时间
        }
        // 获取文件名
        std::string FileName()
        {
            //找到切分文件名称的最后一个/
            auto pos = _filename.find_last_of("/"); 
            if(pos == std::string::npos)
            {
                //当前文件没有路径
                return _filename;
            }
            //找到/后,进行字符串切割
            return _filename.substr(pos+1);
        }
    private:
        std::string _filename;
    };

}

代码测试:

#include "util.hpp"

void FileUtilText(const std::string &filename)
{   
    cloud::FileUtil fu(filename);

    std::cout << fu.FileSize() << std::endl;
    std::cout << fu.LastMTime() << std::endl;
    std::cout << fu.LastATime() << std::endl;
    std::cout << fu.FileName() << std::endl;
}

int main(int argc, char* argv[])
{
    FileUtilText(argv[1]);

    return 0;
}

结果如下:
在这里插入图片描述
注意:这里直接访问到的是文件修改时间、访问时间,其实是时间戳。

文件的读写操作

继续在 util.hpp 文件内完善 FileUtil 类:

  1. bool GetPosLen(std::string *body, size_t pos, size_t len)获取指定位置到指定长度的文件数据
  2. bool GetContent(std::string *body)获取整个文件的数据内容
  3. bool SetContent(const std::string &body)向文件写入数据

下面所有代码是在类中实现的,为了方便演示,只展示当前标题要实现的代码:

#include <iostream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <fstream>

namespace cloud
{
    class FileUtil
    {
    public:  
    	
    	//其他函数...
    	
        // 获取指定位置到指定长度的文件数据
        bool GetPosLen(std::string *body, size_t pos, size_t len)
        {
            size_t fsize = FileSize(); // 获取文件总长度

            // 判断文件总长度是否符合 pos+len 长度
            if (pos + len > fsize)
            {
                std::cout << "get file len is error\n";
                return false;
            }

            std::ifstream ifs;
            ifs.open(_filename.c_str(), std::fstream::binary); // 以二进制的方式打开文件
            if (ifs.is_open() == false)
            {
                std::cout << "read open file false\n";
                return false;
            }
            // 定位文件到pos位置
            ifs.seekg(pos, std::ios::beg);
            body->resize(len); // 扩容字符串
            // 读取数据
            ifs.read(&(*body)[0], len);
            if (ifs.good() == false)
            {
                // 读取出错
                std::cout << "get file content false\n";
                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.c_str(), std::ios::binary); //打开目标文件
            if(ofs.good() == false)
            {
                //打开文件失败
                std::cout << "write open file false\n";
                return false;
            }
            //将原文件内容写入到目标文件中
            ofs.write(&body[0], body.size());
            if(ofs.good() == false)
            {
                //写入失败
                std::cout << "write file file false\n";
                ofs.close();
                return false;
            }
            //写入成功
            ofs.close();
            return true;
        }
    private:
        std::string _filename;
    };
}

测试代码:

void FileUtilText(const std::string &filename)
{   
    cloud::FileUtil fu(filename);
    std::string body;
    fu.GetContent(&body);//获取文件内容到body字符串中

    cloud::FileUtil ufu("./test.txt"); //在当前目录的一个./text.txt文件
    ufu.SetContent(body); //将body字符串内容写入到./text.txt文件中
}

int main(int argc, char* argv[])
{
    FileUtilText(argv[1]);
    return 0;
}

测试结果如下:
在这里插入图片描述

在这里拿 Makefile 文件举例:

将 Makefile 文件内容进行读取,放入到 text.txt文件中。对比两个文件生成哈希值可以看到一模一样,读取文件内容到写入文件过程是没有问题的。

文件压缩与解压缩的实现

依旧是完善 FileUtil 类,实现两个接口:

  1. bool Compress(const std::string &packname)压缩文件
  2. bool UnCompress(const std::string &filename)解压文件

当然,在这里需要引入第三方库 bundle 进行文件的解压缩操作,不知道如何搭环境的小伙伴可以看这篇文章:项目环境搭建

bundle 库下载后,在 src 目录下,引入 bundle.h、bundle.cpp 文件即可:
在这里插入图片描述
实现文件的解压缩功能,在 util.hpp 文件内的 FileUtil 类进行编写:

#include <iostream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <fstream>
#include "bundle.h"

namespace cloud
{
    class FileUtil
    {
    public:  
    	//其他函数...
		
		//压缩文件
        bool Compress(const std::string &packname)
        {
            std::string body;
            //获取文件内容
            if(GetContent(&body) == false)
            {
                std::cout << "compress get file content falsed!\n";
                return false;
            }
            //对刚刚提取的数据进行压缩,使用LZIP压缩格式
            std::string packed = bundle::pack(bundle::LZIP, body);
            //将压缩数据存储到压缩文件中
            if(SetContent(packname) == false)
            {
                std::cout << "compress write packed data falsed!\n";
                return false;
            }
            return true;
        }
        //解压文件
        bool UnCompress(const std::string &filename)
        {
            std::string body;
            //提取压缩包文件数据
            if(GetContent(&body) == false)
            {
                std::cout << "uncompress get file content falsed!\n";
                return false;
            }
            //解压数据
            std::string unpacked = bundle::unpack(body);
            //将解压数据放入到文件中
            if(SetContent(unpacked ) == false)
            {
                std::cout << "uncompress write packed data falsed!\n";
                return false;
            }
            return true;
        }
		
    private:
     std::string _filename;
    };
}

引入了 bundle.cpp 文件后,在 Makefile 文件中也要有对应内容修改:

cloud:cloud.cc util.hpp bundle.cpp
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f cloud

编写测试代码:

#include "util.hpp"

void FileUtilText(const std::string &filename)
{
    std::string packname = filename + ".lz"; //.lz压缩包后缀
    cloud::FileUtil fu(filename);
    // 压缩文件
    fu.Compress(packname);
    cloud::FileUtil pfu(packname);
    // 解压后的文件内容放入到 test.txt
    pfu.UnCompress("./test.txt");
}

int main(int argc, char *argv[])
{
    FileUtilText(argv[1]);

    return 0;
}

文件目录操作

继续完善 FileUtil 类,实现三个接口:

  1. bool Exists()判断文件是否存在
  2. bool CreateDirectory()创建目录
  3. bool ScanDirectory(std::vector<std::string> *arry)获取目录下的文件信息

代码实现:

#include <iostream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <fstream>
#include <experimental/filesystem>
#include "bundle.h"

namespace cloud
{
	// 引用std::experimental::filesystem命名空间
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    public:  
    	//其他函数...
    	
		// 判断文件是否存在
        bool Exists()
        {
            return fs::exists(_filename);
        }
        // 创建文件目录
        bool CreateDirectory()
        {
            // 文件存在,无需创建目录
            if (Exists()) return true;
            // 否则创建这个目录
            return fs::create_directories(_filename);
        }
        // 遍历整个目录,查看文件信息
        bool ScanDirectory(std::vector<std::string> *arry)
        {
            // 迭代遍历目录下的文件
            for (auto &p:fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true) continue;// 如果是一个目录,不再往下遍历
                // 将带有路径的文件名称push到arr数组中
                arry->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
     std::string _filename;
    };
}

在这里是直接调用了 C++17 的 filesystem 函数,对此在Makefile 中,要连接对应的库:-lstdc++fs

cloud:cloud.cc util.hpp bundle.cpp
	g++ -o $@ $^ -lpthread -std=c++11 -lstdc++fs

.PHONY:clean
clean:
	rm -f cloud

测试代码:

#include "util.hpp"

void FileUtilText(const std::string &filename)
{
    cloud::FileUtil fu(filename);
    fu.CreateDirectory(); //创建文件目录
    std::vector<std::string> array;
    fu.ScanDirectory(&array); //遍历目录文件,存储到array数组中
    // 遍历目录文件
    for (auto &e : array)
        std::cout << e << std::endl;
}

int main(int argc, char *argv[])
{
    FileUtilText(argv[1]);

    return 0;
}

测试结果如下:
在这里插入图片描述
先前是没有 test 目录的,运行程序后,直接创建 test 目录。

接下来在 test 目录下创建一些文件、目录,再次运行 cloud ,查看结果:
在这里插入图片描述
可以看到在 test 目录下是文件的都遍历了一遍,abc 目录没有进行遍历。

至此,FileUtil 类主要功能就实现完了。接下来是 JsonUtil 类的实现:

Json 实用工具类设计

JsonUtil 类实现依旧是在 util.hpp 文件中进行编写。其类中主要实现两个接口:

  1. static bool Serialize(const Json::Value &root, std::string *str)序列化
  2. static bool UnSerialize(const std::string &str, Json::Value *root)反序列化

使用前需要下载 Json 第三方库,具体的下载方式可以参考这篇文章:Json下载与使用

代码实现:

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <jsoncpp/json/json.h>

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\n";
            return false;
        }
        //将序列化后的字符串保存到str中
        *str = ss.str(); 

        return true;
    }
    //反序列化
    static bool UnSerialize(const std::string &str, Json::Value *val)
    {
        Json::CharReaderBuilder crb;
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

        std::string err;
        //将反序列化内容存储到str中
        bool ret = cr->parse(str.c_str(), str.c_str()+str.size(), val, &err); 
        if(ret == false)
        {
            std::cout << "parse error:" << err << std::endl;
            return false;
        }
        return true;
    }
};

为了编译时不报错,需要对 Makefile 文件进行修改,加入 -ljsoncpp选项:

cloud:cloud.cc util.hpp bundle.cpp
	g++ -o $@ $^ -lpthread -lstdc++fs -std=c++14 -ljsoncpp

测试代码:

void JsonUtilTest()
{
    const char *name = "张三";
    int age = 18;
    float score[] = {90.5, 77, 100};

    Json::Value root;
    root["姓名"] = name;
    root["年龄"] = age;
    root["成绩"].append(score[0]);
    root["成绩"].append(score[1]);
    root["成绩"].append(score[2]);

    std::string json_str;
    cloud::JsonUtil::Serialize(root, &json_str); //序列化操作
    std::cout << json_str << std::endl;

    Json::Value val;
    cloud::JsonUtil::UnSerialize(json_str, &val);//反序列化操作
    std::cout << val["姓名"].asString() << std::endl; 
    std::cout << val["年龄"].asInt() << std::endl; 

    for(auto &e : val["成绩"])
    {
        std::cout << e << std::endl; 
    }
}

int main(int argc, char *argv[])
{
    JsonUtilTest();
    return 0;
}

测试结果如下:
在这里插入图片描述

编译优化

云备份项目引入了 bundle 第三方压缩库,整个 bundle.cpp 内容还是比较大的。

为了代码编写的正确性,我们每写完一段代码后都要进行测试。每次测试都要进行整体代码的编译,包括 bundle库。

由于 bundle.cpp 编译时长比较久,难免会很难受。对此,下面对 bundle.cpp 直接生成静态库,编译时直接链接就可以了。这样就可以很好的避免编译等待时间过久的问题。

关于动静态库的制作,可以参考小编的这篇文章:动静态库的介绍

下面开始进行操作:

  1. 使用 g++ 编译器,将 bundle.cpp 源文件生成 .o文件
g++ -c bundle.cpp -o bundle.o -std=c++11
  1. 制作 bundle 静态库
 ar -cr libbundle.a bundle.o

在这里插入图片描述
只要看到 libbundle.a 文件,就说明制作成功了。对于 src 目录下的 bundle.cpp、bundle.o,可以直接删除。

  1. 创建 lib 目录,将 libbundle.a 剪切到 lib 目录下即可

在这里插入图片描述
4. 更改 Makefile 内容,编译所用到的库

cloud:cloud.cc util.hpp
	g++ -o $@ $^ -lpthread -lstdc++fs -std=c++14 -ljsoncpp -L./lib -lbundle

.PHONY:clean
clean:
	rm -f cloud

此后,我们对整个项目进行编译,无需再对 bundle.cpp 进行处理,可以节省很多时间。

后续内容会持续更新,喜欢的老铁可以点赞、收藏加关注,感谢大家的观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2141836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【吊打面试官系列-MySQL面试题】简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别?

大家好&#xff0c;我是锋哥。今天分享关于【简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别&#xff1f; MyISAM&#xff1a; 不支持事务&#xff0c;但是每次查…

跳出大厂圈子——普通程序员如何开启逆袭之路

时间&#xff1a;2024年09月16日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频&#xff1a;喜马拉雅 大家好&#xff0c;欢迎来到“小蒋聊技术”&#xff01;今天咱们聊点特别现实的事儿——普通程序员的出路。互联网时代…

化学实验室器具识别系统源码分享

化学实验室器具识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

android设置实现广告倒计时功能

文章目录 CountDownTimer基本使用增加基础BaseActivity增加固定活动 在Android中&#xff0c;CountDownTimer 是一个用于计时的类&#xff0c;它允许你在指定的时间段内执行某些操作。通常用于倒计时功能&#xff0c;例如显示一个倒计时进度条或者在倒计时结束后执行某个动作。…

采用qt做一个命令行终端

qt做一个类似系统命令行终端的工具&#xff0c;方便集成到自己的软件里使用&#xff0c;这样能保证软件的整体性&#xff0c;而且是真正的做到和系统命令行终端一样的交互方式&#xff0c;而不是单独搞个编辑框的方式输入命令&#xff08;大部分博客都是做成这个样子&#xff0…

不善言辞的程序员适合做项目经理吗?

项目经理的角色需要承担多重任务&#xff0c;包括团队协调、资源调配、风险管理、沟通与汇报等。因此&#xff0c;很多人认为项目经理需要较强的沟通能力和外向性格。然而&#xff0c;不善言辞的程序员是否适合这一职位&#xff0c;实际上取决于多个因素。以下从不同角度进行分…

程序设计题(25-32)

第二十五题 题目 请编写函数fun&#xff0c;其功能是:在一个含有11个四位数的数组中&#xff0c;统计出这些数的奇数、偶数个数&#xff0c;然后计算出个数多的那些数的算数平均值并由函数返回&#xff0c;个数通过yy传回。 例如&#xff0c;若11个数据为:1101, 1202, 1303,…

AI+RPA 实战揭秘:DrissionPage 助力 CSDN 热榜数据抓取与 AI 结合

在前一篇文章《AIRPA&#xff1a;开启智能自动化新时代》 发布之后&#xff0c;今天我们将以实战的方式深入介绍数据分析中的RPA究竟是怎样抓取数据的&#xff0c;以及它与 AI 又是如何紧密结合的。首先&#xff0c;让我们来认识一个重要的工具包 ——DrissionPage。 一、Dris…

【例题】lanqiao301 实现基数排序

输入输出样例 输入 6 7 1 4 8 5 2输出 1 2 4 5 7 8解题思路 翻译&#xff1a;就是从个位到十位、……比较大小。 代码 nint(input()) alist(map(int,input().split())) a.sort() print( .join(map(str,a)))

Linux: network: delay and burst

最近遇到一个false-network的问题&#xff0c;抓到的网络包的特征是&#xff0c;有十几秒的延迟&#xff0c;然后来了一个burst。这个现象说明这个包肯定是缓存在了一个地方&#xff0c;但是具体是什么地方&#xff0c;就需要抓包确定。 这次的缓存的地方是虚拟机内部。由于一…

【刷题】Day5--数字在升序数组中出现的次数

Hi! 今日份刷题~ 数字在升序数组中出现的次数_牛客题霸_牛客网 我感觉题目简单&#xff0c;我的解答也很简单&#xff0c;二分法遗忘&#xff0c;有时间复习一下尝试新的解法。 /*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的…

轻松上手Cursor,体验丝滑编程

最近Cursor大火。 作为一名程序员&#xff0c;理应接触接触&#xff0c;了解了解。 好吧&#xff01;这一了解&#xff0c;我直接用Cursor替换了用了好几年的vscode了。 下面我把体验Cursor的过程总结了一下。你看看。 Cursor是啥 Cursor是一款革命性的AI驱动代码编辑器&a…

rocky Linux 9.4系统配置zabbix监控MySQL主从复制状态与配置钉钉告警

MySQL主从复制原理&#xff1a; 1. 主从复制的基本概念 主服务器&#xff08;Master&#xff09;&#xff1a;负责处理所有的写操作&#xff08;INSERT、UPDATE、DELETE&#xff09;&#xff0c;并将这些操作记录到二进制日志&#xff08;binary log&#xff09;中。 从服务器…

深度揭秘:日志打印的艺术与实战技巧,让你的代码会说话!

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 &#x1f341;日志&#x1f342;日志分模块实现讲解&#x1f343;日志等级的实现&#x1f965;日志时间*时间的获取* &#x1f308;文…

Nginx搭建直播服务器,并用rtmp,http-flv,hls三种模式拉流观看直播的流程

一、首先搭建直播服务器 环境widows&#xff0c;并且已经集成了 &#xff1a;nginx-http-flv-module模块 nginx.conf配置如下&#xff1a; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #error…

Vue3中集成高德地图并实现平移缩放功能

大家好&#xff0c;随着前端技术的不断发展&#xff0c;地图应用在我们的项目中越来越常见。本文将介绍如何在Vue3项目中集成高德地图&#xff0c;并通过简单的配置实现地图的平移和缩放功能。 实现步骤 1、申请高德地图密钥&#xff08;Key&#xff09;&#xff08;已有key可…

Git使用详解:从安装到精通

前言 什么是Git Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码文件&#xff08;Java类、xml文件、html页面等&#xff09;&#xff0c;在软件开发过程中被广泛使用。 可以理解&#xff1a; git是一个管理源代码的工具&#xff0c;主要用于企业团队开…

openwrt wsdd模块介绍

wsdd简介 wsdd是一个实现Web服务发现主机守护进程的工具。这使得&#xff08;如Samba&#xff09;主机&#xff0c;像你的本地网络存储设备&#xff0c;能够被Windows这样的Web服务发现客户端找到。 目的 由于Windows不再支持NetBIOS发现&#xff0c;wsdd使用Web服务发现方法使…

密码学---easy_hash

题目出处&#xff1a;首页 - Bugku CTF ✨打开题目有两个文件 ✨打开flag.py后开始分析所给的代码 import hashlib # 导入 hashlib 模块&#xff0c;用于计算哈希值 from multiprocessing import Pool # 从 multiprocessing 模块导入 Pool 类&#xff0c;用于多进程处理d…

istio中如何使用serviceentry引入外部服务

假设需要引入一个外部服务&#xff0c;外部服务ip为10.10.102.90&#xff0c;端口为32033. 引入到istio中后&#xff0c;我想通过域名gindemo.test.ch:9090来访问这个服务。 serviceentry yaml内容如下&#xff1a; apiVersion: networking.istio.io/v1beta1 kind: ServiceEn…