项目实现:云备份②(文件操作、Json等工具类的实现)
云备份
- 前言
- 文件操作实用工具类设计
- 文件属性的获取
- 文件的读写操作
- 文件压缩与解压缩的实现
- 文件目录操作
- Json 实用工具类设计
- 编译优化
前言
如果有老铁不知道当前项目实现的功能是什么的话,可以先移步这篇文章内容: 云备份项目的介绍
其中介绍了云备份项目的基本功能、环境搭建的详情。
文件操作实用工具类设计
文件属性的获取
创建 src 目录,在 src 目录下进行 util.hpp
文件的代码编写,实现文件工具类:
int64_t FileSize()
:获取文件大小time_t LastMTime()
:获取文件最后一次的修改时间time_t LastATime()
:获取文件最后一次的访问时间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 类:
bool GetPosLen(std::string *body, size_t pos, size_t len)
:获取指定位置到指定长度的文件数据bool GetContent(std::string *body)
:获取整个文件的数据内容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 类,实现两个接口:
bool Compress(const std::string &packname)
:压缩文件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.cppg++ -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.txtpfu.UnCompress("./test.txt");
}int main(int argc, char *argv[])
{FileUtilText(argv[1]);return 0;
}
文件目录操作
继续完善 FileUtil 类,实现三个接口:
bool Exists()
:判断文件是否存在bool CreateDirectory()
:创建目录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.cppg++ -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 文件中进行编写。其类中主要实现两个接口:
static bool Serialize(const Json::Value &root, std::string *str)
:序列化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.cppg++ -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 直接生成静态库,编译时直接链接就可以了。这样就可以很好的避免编译等待时间过久的问题。
关于动静态库的制作,可以参考小编的这篇文章:动静态库的介绍
下面开始进行操作:
- 使用 g++ 编译器,将 bundle.cpp 源文件生成
.o
文件
g++ -c bundle.cpp -o bundle.o -std=c++11
- 制作 bundle 静态库
ar -cr libbundle.a bundle.o
只要看到 libbundle.a 文件,就说明制作成功了。对于 src 目录下的 bundle.cpp、bundle.o,可以直接删除。
- 创建 lib 目录,将 libbundle.a 剪切到 lib 目录下即可
4. 更改 Makefile 内容,编译所用到的库
cloud:cloud.cc util.hppg++ -o $@ $^ -lpthread -lstdc++fs -std=c++14 -ljsoncpp -L./lib -lbundle.PHONY:clean
clean:rm -f cloud
此后,我们对整个项目进行编译,无需再对 bundle.cpp 进行处理,可以节省很多时间。
后续内容会持续更新,喜欢的老铁可以点赞、收藏加关注,感谢大家的观看!
相关文章:

项目实现:云备份②(文件操作、Json等工具类的实现)
云备份 前言文件操作实用工具类设计文件属性的获取文件的读写操作文件压缩与解压缩的实现文件目录操作 Json 实用工具类设计编译优化 前言 如果有老铁不知道当前项目实现的功能是什么的话,可以先移步这篇文章内容: 云备份项目的介绍 其中介绍了云备份项…...

内网穿透技术总结
内网穿透是一种网络技术,通过它可以使外部网络用户访问内部网络中的设备和服务。一般情况下,内网是无法直接访问的,因为它位于一个封闭的局域网中,无法从外部访问。而通过内网穿透,可以将内部网络中的设备和服务暴露在…...

Git使用—把当前仓库的一个分支push到另一个仓库的指定分支、基于当前仓库创建另一个仓库的分支并推送到对应仓库(mit6828)
把学习过程中遇到的Git问题汇总如下(后续学习遇到问题会及时更新此专栏): Git原理及常用命令小结——实用版(ing......)、Git设置用户名邮箱-CSDN博客 解决git每次push代码到github都需要输入用户名以及密码-CSDN博客…...

windows11+ubuntu20.04.6双系统安装
记录win11和ubuntu20.04.6在单个硬盘上安装的主要流程 系统说明 BIOS模式: UEFI 硬盘: 1TB固态 内存: 32GB 步骤 1、 准备两个不小于16GB的U盘,一个用于装Windows,一个用于装ubuntu,注意8G的U盘虽然能够…...

如何通过 PhantomJS 模拟用户行为抓取动态网页内容
引言 随着网页技术的不断进步,JavaScript 动态加载内容已成为网站设计的新常态,这对传统的静态网页抓取方法提出了挑战。为了应对这一挑战,PhantomJS 作为一个无头浏览器,能够模拟用户行为并执行 JavaScript,成为了获…...
ARM驱动学习之8 动态申请字符类设备号
ARM驱动学习之8 动态申请字符类设备号 KernelCode: • 字符设备函数在文件“include/linux/fs.h”中 • alloc_chrdev_region() 是动态分配主次设备号。 • 宏定义MAJOR提取dev_t数据中的主设备号源码: /*** alloc_chrdev_region() - register a range of char dev…...

TCP.IP四层模型
一、TCP/IP模型协议分层 1、应用层: 2、传输层: TCP:传输控制协议 UDP:用户数据报协议 3、网络层: IP: 国际协议(IP地址) ICMP: 互联网控制消息协议(互联网…...
极狐GitLab DevSecOps 功能合集(七大安全功能)
极狐GitLab 是 GitLab 在中国的发行版,专门面向中国程序员和企业提供企业级一体化 DevOps 平台,用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规,而且所有的操作都是在一个平台上进行,省事省心省钱。可以一键安装极狐GitL…...

进阶SpringBoot之异步任务、邮件任务和定时执行任务
SpringBooot 创建 Web 项目 异步任务: service 包下创建 AsyncService 类 Async 异步方法 Thread.sleep(3000) 停止三秒,捕获异常 package com.demo.task.service;import org.springframework.scheduling.annotation.Async; import org.springfram…...

【设计模式-桥接】
定义 桥接模式(Bridge Pattern)是一种结构型设计模式,它通过将抽象部分与实现部分分离,使它们都可以独立地变化。桥接模式的关键在于将类的抽象部分与其实现部分解耦,以便两者可以独立地变化。这种设计模式的一个主要…...

JVM JMM 专题篇 ( 12000 字详解 )
一:JVM 简介 JVM 是 Java Virtual Machine 的简称,意为 Java 虚拟机,虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。 常见的虚拟机:JVM、VMwave、Virtual Box,JVM 和其…...

【C++】—— list 模拟实现
【C】—— list 模拟实现 1 list 基础结构2 默认构造3 迭代器3.1 整体框架3.2 成员函数3.3 begin() 与 end() 的实现3.4 operator-> 的实现3.5 const 迭代器3.5.1 const 迭代器为什么命名 const_iterator3.5.2 const 迭代器的实现3.5.3 合并两个迭代器 4 源码 1 list 基础结…...
Redis主要问题
Redis redis是单线程,基于内存操作,所以执行很快。,与网络延迟有关。 还是买早餐的例子,从早餐店买一个包子,首先早餐店得还有包子没有卖完,然后卖出,包子数量-1,重新设定早餐店包…...

vue3 ref的用法及click事件的说明
1、ref可以定义一个简单的属性,也可以是一个复杂的列表、数组等等。 2、为什么要使用 ref?简单的let个变量不行吗?const个变量不行吗? 其实这个跟vue的响应式的系统有关,官方的说明如下: 3、为 ref() 标注…...

通信工程学习:什么是HFC混合光纤同轴电缆
HFC:混合光纤同轴电缆 HFC(Hybrid Fiber Coaxial)混合光纤同轴电缆是一种结合了光纤与同轴电缆的宽带接入网技术。以下是对HFC混合光纤同轴电缆的详细解释: 一、HFC混合光纤同轴电缆的定义与概述 定义:HFC是一种结合光…...

怎么浏览URL的PDF文件呢
最近发现PDF文件网页端打开就是丑,不知道怎么办 1. 看着实在不舒服,用chorm的插件 然后原本本地用的也是2345pdf阅读器 2. 之后也下载了adobe pdf的桌面阅读器 2345打开是这个样子 这个是现在啦 如果要一些安装包什么的,评论见~ 最…...

【2025届华为秋招机考三道编程题之一】华为校招留学生软件开发工程师-真题机考笔试/(200分)- 跳格子3(Java JS Python C)
华为校招机考的题型: 编程:软件测试工程师,算法,OD岗,三道编程题不限语言【C,Python,Java】 校招:600分 120分钟,100/200/300 社招:400分 150分钟…...
高性能缓存利器:Caffeine 在 Spring Boot 中的应用
在现代应用程序中,缓存是提高数据检索速度、减少对数据库或其他数据源访问次数的重要手段。Spring Cache 提供了多种缓存实现方式,而在我们的 Spring Boot 项目中,我们选择了 Caffeine 作为默认的缓存库。 Caffeine 简介 Caffeine 是一个基…...
pWnOS的第二种全新解法(ssh私钥破解、webmin漏洞提权)
端口 端口扫描内容请看:vulnhub(8):pWnOS(还没信息收集就已经成功打点)-CSDN博客 打点 ssh登录公钥收集 ./2017.pl 192.168.234.116 10000 /home/vmware/.ssh/authorized_keys 0 ./2017.pl 192.168.234.11…...

Maven入门学习笔记
一、maven介绍 Maven是一款自动化构建工具,专注服务于JAVA平台的项目构建和依赖管理。在javaEE开发的历史上构建工具的发展也经历了一系列的演化和变迁。 管理jar包 当我们使用SSM之后我们就需要使用非常多的jar包 没有maven找jar包非常的麻烦。 使用maven下载…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...

计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...