【云备份】服务端工具类实现
1.文件实用工具类设计
不管是客户端还是服务端,文件的传输备份都涉及到文件的读写,包括数据管理信息的持久化也是如此,因此首先设 计封装文件操作类,这个类封装完毕之后,则在任意模块中对文件进行操作时都将变的简单化。
文件系统库 - cppreference.com
文件实用工具类FileUtil
主要包含以下成员:
namespace cloud {//注意:我们下面这些接口的名称可能会与系统的那些接口的名字重复,所以最好创建名称空间来避免名称污染class FileUtil{public:FileUtil(const std::string& filename) :_filename(filename);//主要针对普通文件的接口int64_t FileSize(); // 获取文件大小,这里使用64位的有符号的int,防止文件过大导致文件大小显示异常time_t LastModtime(); // 获取文件最后一次修改时间time_t LastAccTime(); // 获取文件最后一次访问时间std::string FileName(); // 获取文件路径中的纯文件名称 a/b/c/test.cc --> test.ccbool SetContent(const std::string& body); // 向文件写入数据bool GetContent(std::string* body); // 获取文件内容bool GetPosLen(std::string* body, size_t pos, size_t len); // 获取文件指定区间的数据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:获取文件大小;
- LastModtime:获取文件最后一次修改时间;
- LastAccTime:获取文件最后一次访问时间,这个是为了我们后面的热点文件管理,如果一个文件很久都没有被访问过,我们就要对它进行压缩处理
- FileName:获取文件路径中的纯文件名称,比如说我们传入了路径名 a/b/c/test.cc ,使用Filename只获取最后的文件名test.cc;
- GetPosLen:获取文件指定区间的数据;这个主要是为了我们后面的断点续传功能。
- GetContent:获取文件内容;这里函数参数使用指针的原因是它是一个输入输出型参数,文件的内容都会被传入到这个指针指向的那块地方。当然你使用引用也是可以的。
- SetContent:向文件写入数据;这里函数参数使用引用的原因是它是一个输入型参数,使用引用能提高效率
- Compress:压缩文件;
- UnCompress:解压缩文件;
- Exists:判断文件是否存在;
- Remove:删除文件;
- CreateDirectory:创建文件目录;
- GetDirectory:浏览指定目录下的所有文件;我们说Linux下一切皆是文件,目录也是。
我们这里不单单对普通文件进行处理,我们对目录文件也设计了接口。
首先我们打开我们的云服务器看看
这些都是我们之前实验下来的那些文件啊,我们现在把不必要的文件进行删除
我们创建一个util.hpp,把我们上面的东西给搞进去。
2.文件实用工具类实现
2.1.获取文件属性操作的实现
- FileSize()函数的实现
int64_t FileSize(); // 获取文件大小,这里使用64位的有符号的int,防止文件过大导致文件大小显示异常
这个需要我们了解一下不太常用的接口函数——stat
man 2 STAT
stat函数用于获取与指定路径名相关联的文件或目录的属性,并将这些属性填充到一个struct stat结构体中。以下是stat函数的函数原型:
int stat(const char *pathname, struct stat *statbuf);
- pathname是要获取属性的文件或目录的路径名;
- statbuf是一个指向struct stat结构体的指针,用于存储获取到的属性信息;
stat函数返回一个整数值,如果操作成功,返回0;
如果出现错误,返回-1,并设置errno全局变量以指示错误的类型。
- struct stat类型
我们来看看能获取到什么文件信息!
在C语言中,struct stat是一个用于表示文件或文件系统对象属性的结构体类型。
这个结构体通常用于与文件和目录相关的操作,例如获取文件的大小、访问权限、最后修改时间等信息。
struct stat类型的定义通常由操作系统提供,因此其具体字段可能会因操作系统而异。
以下是一个典型的struct stat结构体的字段,尽管具体字段可能会因操作系统而异:
struct stat {dev_t st_dev; // 文件所在设备的IDino_t st_ino; // 文件的inode号mode_t st_mode; // 文件的访问权限和类型nlink_t st_nlink; // 文件的硬链接数量uid_t st_uid; // 文件的所有者的用户IDgid_t st_gid; // 文件的所有者的组IDoff_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; // 文件占用的块数
};
我们也可以在上面的界面往下滑!看看我的系统的真实情况是啥
现在我们就应该知道怎么设计这个FileSize函数了吧!因为我们这有sz_size函数
int64_t FileSize() // 获取文件大小,这里使用64位的有符号的int,防止文件过大导致文件大小显示异常
{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get FileSize Failed!!" <<std::endl;return -1;}return st.st_size;
}
- LastModtime()和LastAcctime()的实现
其实这个和上面的FileSize()是差不多的,只不过……算了先开下面这个
struct timespec
是 C 语言中用于表示高精度时间的结构体,尤其在 Linux/Unix 系统中广泛使用。它设计用来存储时间的秒(tv_sec
)和纳秒(tv_nsec
)分量,提供比传统的 time_t
(仅秒级)更高的时间精度。
#include <time.h> // 需要包含的头文件struct timespec {time_t tv_sec; // 秒(自 1970-01-01 00:00:00 UTC 的秒数)long tv_nsec; // 纳秒(0 到 999,999,999)
};
来看看怎么使用
测试函数——获取当前时间(纳秒级)
#include <stdio.h>
#include <time.h>int main() {struct timespec ts;// 获取系统实时时钟(CLOCK_REALTIME)if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {perror("clock_gettime");return 1;}printf("Current time: %lld seconds, %ld nanoseconds\n",(long long)ts.tv_sec, ts.tv_nsec);return 0;
}
现在我们就应该知道怎么使用了
time_t LastModtime() // 获取文件最后一次修改时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastModtime Failed!!" <<std::endl;return -1;}return st.st_mtim.tv_sec;}time_t LastAcctime() // 获取文件最后一次访问时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastAccTime Failed!!" <<std::endl;return -1;}return st.st_atim.tv_sec;}
- Filename()的实现
我们知道我们的路径名都是用/来进行分割的,比如./a/b.txt,那么我们只需要获取最后一个/后面到最末尾的东西即可
std::string FileName() // 获取文件路径中的纯文件名称 a/b/c/test.cc --> test.cc{size_t pos = _filename.find_last_of("/");//寻找最后一个/if (pos == std::string::npos)//没找到,说明没有/{return _filename;}return _filename.substr(pos + 1);//从pos+1位置截取到末尾}
- 小测试
我们目前写的代码就是现在这样子
util.hpp
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>namespace cloud {//注意:我们下面这些接口的名称可能会与系统的那些接口的名字重复,所以最好创建名称空间来避免名称污染class FileUtil{public:FileUtil(const std::string& filename):_filename(filename){}//主要针对普通文件的接口int64_t FileSize() // 获取文件大小,这里使用64位的有符号的int,防止文件过大导致文件大小显示异常{struct stat st;int re=stat(_filename.c_str(),&st);if(re<0)//stat函数获取文件属性失败了{std::cout<<"Get FileSize Failed!!"<<std::endl;return -1;}return st.st_size;}time_t LastModtime() // 获取文件最后一次修改时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastModtime Failed!!" << std::endl;return -1;}return st.st_mtim.tv_sec;}time_t LastAcctime() // 获取文件最后一次访问时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastAcctime Failed!!" << std::endl;return -1;}return st.st_atim.tv_sec;}std::string FileName() // 获取文件路径中的纯文件名称 a/b/c/test.cc --> test.cc{size_t pos=_filename.find_last_of("/");//寻找最后一个/if(pos==std::string::npos)//没找到,说明没有/{return _filename;}return _filename.substr(pos+1);//从pos截取到末尾}private:std::string _filename; // 文件名--包含路径};
};
代码写到这里我们,我们必须进行测试我们的代码对不对,要不然写到后面我们一直报错就不好了
cloud.cc
#include"util.hpp"
void FileUtilTest(const std::string &filename)
{cloud::FileUtil fu(filename);std::cout<<fu.FileSize()<<std::endl;std::cout<<fu.LastModtime()<<std::endl;std::cout<<fu.LastAcctime()<<std::endl;std::cout<<fu.FileName()<<std::endl;}
int main(int argc,char*argv[])
{ FileUtilTest(argv[1]);
}
makefile
cloud:cloud.cc util.hppg++ $^ -o $@
.PHONY:clean
clean:rm -f cloud
我们编译运行一下
这个就很好了!!
我们去看看
我们只是在main函数里面添加了一句retrun 0,就发现它们都修改了
我们再看Filename()的测试,也是很不错!!
这就说明我们完成的代码就很好了
接下来我要讲一下git,如果只想看项目的可以往下一节去了
首先我们先创建本地仓库
然后配置本地仓库
接着提交,发现git add *报错了
我们有两种解决方法,但是我只选择下面这种
直接包含子目录代码(不保留 Git 历史)
如果这些目录不需要独立维护(例如你只是复制了代码,不需要跟踪它们的更新),可以删除它们的 .git
文件夹,再添加到父仓库。
删除子目录中的 .git
文件夹:
rm -rf bundle/.git cpp-httplib/.git
重新添加所有文件,提交更改
接下来要把它推送到远程仓库里面去,首先我们要先创建一个远程仓库
有了本地仓库和远程仓库后,可以将二者关联起来,以便推送和拉取代码:
在本地仓库中,执行以下命令来添加远程仓库的地址:
git remote add origin <远程Git仓库地址>
其中,<远程Git仓库地址>是你的远程Git仓库的网址。
对于如何获取远程Git仓库地址,我们举例说明:
比如,你的远程Git仓库地址为:
https://github.com/your/your.git
那么你在本地使用“git remote add origin”指令的语法就应该是:
git remote add origin https://github.com/your/your.git
执行这条指令之后,你的本地项目就与远程Git仓库建立了连接,你就可以开始对你的代码进行版本追踪和协作开发了。
-
检查关联是否成功,执行以下命令:
git remote -v
- 推送到远程仓库
关联完成后,可以将本地仓库中的代码推送到远程仓库中:
git push -f origin master
注意:-f是强制的意思
这样就将本地仓库中的代码推送到了远程仓库的 master 分支上。如果是第一次推送,可能需要输入用户名和密码进行身份认证。
这很好了
git remote add origin的一些常用操作
1. 更改默认的远程仓库
在项目中可能存在多个远程仓库,如果你想更改默认仓库,可以使用如下指令:git remote set-url origin <新的远程Git仓库地址>
2. 查看当前的远程仓库
如果你想查看当前项目的远程仓库,可以使用如下指令:git remote -v
3. 删除远程仓库
如果你需要删除已经添加的远程仓库,可以使用如下指令:git remote rm origin
执行这条指令之后,Git就会将已经添加的名为“origin”的仓库删除。
2.2.文件的读写操作的实现
GetPosLen()函数
bool GetPosLen(std::string* body, size_t pos, size_t len)
{std::ifstream ifs;ifs.open(_filename, std::ios::binary);//打开文件,以二进制方式来读取数据if (ifs.is_open() == false)//打开失败{std::cout << "GetPosLen: open file failed!" << std::endl;return false;}size_t fsize = this->FileSize();//获取文件大小if (pos + len > fsize)//超过文件大小了{std::cout << "GetPosLen: get file len error" << std::endl;return false;}ifs.seekg(pos, std::ios::beg); // 将文件指针定位到pos处body->resize(len);//把存储读取的数据的载体的大小修改到够大的ifs.read(&(*body)[0], len);//读取数据,注意这里body是指针,需要先解引用if (ifs.good() == false)//上次读取出错了{std::cout << "GetPosLen: get file content failed" << std::endl;ifs.close();return false;}ifs.close();return true;
}
GetContent()
bool GetContent(std::string* body)
{size_t fsize = FileSize();return GetPosLen(body, 0, fsize);
}
SetContent()
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;
}
接下来我们来测试一下
cloud.cc
#include"util.hpp"
void FileUtilTest(const std::string &filename)
{cloud::FileUtil fu(filename);std::string body;fu.GetContent(&body);cloud::FileUtil nfu("./hello.txt");nfu.SetContent(body);}
int main(int argc,char*argv[])
{FileUtilTest(argv[1]);return 0;
我们进去看看
简直一模一样。但是看着一样是不一定一样的,我们需要借助工具来看看是不是一样的
很显然是一样的了。
同样的,在这里,我们还是需要使用git来进行备份一下
2.3.文件压缩和解压缩操作
接下来来实现Compress函数和Uncompress函数。这是非常简单的。
bool Compress(const std::string& packname)
{// 1.获取源文件数据std::string body;if (this->GetContent(&body) == false)//源文件数据都存储在body里面{//获取源文件数据失败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 (this->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;
}
由于我们bundle库是第三方库,所以不要忘记了添加头文件
除此之外还是不够的,我们还需要将bundle.h和bundle.cpp拷贝到当前目录下来
好,现在我们来测试一下
makefile
cloud:cloud.cc util.hpp bundle.cppg++ $^ -o $@ -lpthread
.PHONY:clean
clean:rm -f cloud
cloud.cc
#include"util.hpp"
void FileUtilTest(const std::string &filename)
{cloud::FileUtil fu(filename);fu.Compress(filename+".lz");cloud::FileUtil pfu(filename+".lz");pfu.UnCompress("hello.txt");}
int main(int argc,char*argv[])
{FileUtilTest(argv[1]);return 0;
}
我们编译运行一下
这些警告不管。
2.4.目录文件操作实现
我们先来认识一个接口——scandir,c语言里面浏览一个目录的内容
看到三级指针就蒙蔽了!所以这个接口用起来是不太好用的,这个时候就需要借助C++17所支持的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 库来执行文件系统操作。
大家想要了解具体内容请去:文件系统库 - cppreference.com
- ScanDirectory()函数的实现
我们点开这个
我们看看例子
可能大家看不太懂,我加注释给你们看看
// 使用实验性文件系统库(C++17 前需用 experimental 命名空间,C++17 后改为 std::filesystem)
#include <experimental/filesystem>
#include <fstream> // 文件流操作(如创建文件)
#include <iostream> // 输入输出流// 为实验性文件系统库定义别名 fs,简化代码
namespace fs = std::experimental::filesystem;int main() {// 1. 创建嵌套目录 "sandbox/a/b"// create_directories 会递归创建所有不存在的父目录fs::create_directories("sandbox/a/b");// 2. 在 sandbox 目录下创建两个空文件// 使用 std::ofstream 的构造函数直接创建文件(文件内容为空)std::ofstream{"sandbox/file1.txt"}; // 创建 file1.txtstd::ofstream{"sandbox/file2.txt"}; // 创建 file2.txt// 3. 遍历 sandbox 目录下的所有条目并打印路径// directory_iterator 用于遍历目录内容// entry 是目录条目,包含文件/子目录的信息std::cout << "目录内容:\n";for (const fs::directory_entry& entry : fs::directory_iterator{"sandbox"}) {// 直接输出 entry 会显示其完整路径(需支持 operator<< 重载)std::cout << " " << entry << '\n'; }// 4. 递归删除整个 sandbox 目录及其内容// remove_all 会删除目录、子目录和所有文件fs::remove_all("sandbox");return 0;
}
这样子就很简单易懂了。
我们完全可以将其复制下来,就能知道怎么使用这个代码了。
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;}
注意:迭代器返回的p不是string,不能直接将p添加进array里面,我们需要使用path将其转换成string
我们进去看看怎么使用
我们发现它打印的都是带有\的文件名,这就是我们要找的。
此外注意relative_path函数返回的是一个path对象
而我们是要string的,所以我们还是需要借助path的接口string(),这个自己去官网看
- Exists()的实现
我们去刚刚那个网站,就很容易看到下面这个
点进去看就明白了
也就是下面这个
bool exists(const path& p);
检查给定的路径(path
)是否对应一个实际存在的文件或目录。
返回值:
-
若路径
p
对应的文件或目录存在,返回true
;否则返回false
。
现在我们很容易就写出下面这个
namespace fs = std::experimental::filesystem;
bool Exists()
{return fs::exists(_filename);
}
- Remove()的实现
点进去看看
我们借助DeepSeek帮我们解析这个函数的用法
-
remove
:删除单个文件或空目录(类似 POSIX 的remove
)。-
符号链接处理:删除符号链接本身,而非其指向的目标。
-
限制:若路径是目录,必须为空才能删除,否则失败。
-
bool remove(const path& p);
-
p
– 要删除的文件或空目录的路径。
-
返回值:
-
成功删除或文件不存在时返回
true
。 -
若路径存在但删除失败(如目录非空或权限不足),返回
false
。
-
bool Remove()
{if(Exists() == false){return true;}remove(_filename.c_str());return true;
}
- CreateDirectory()的实现
还是熟悉的配方,我不过多说,大家不懂的可以去这里看:Filesystem library - cppreference.com
bool CreateDirectory()
{if (Exists()) return true;//如果存在,则直接返回true即可return fs::create_directories(_filename);//不存在的话,创建一个文件
}
注意要包含头文件#include <experimental/filesystem>
接下来来测试一下
Util.hpp
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include"bundle.h"
#include <experimental/filesystem>namespace cloud {//注意:我们下面这些接口的名称可能会与系统的那些接口的名字重复,所以最好创建名称空间来避免名称污染namespace fs = std::experimental::filesystem;class FileUtil{public:FileUtil(const std::string& filename):_filename(filename){}//主要针对普通文件的接口int64_t FileSize() // 获取文件大小,这里使用64位的有符号的int,防止文件过大导致文件大小显示异常{struct stat st;int re=stat(_filename.c_str(),&st);if(re<0)//stat函数获取文件属性失败了{std::cout<<"Get FileSize Failed!!"<<std::endl;return -1;}return st.st_size;}time_t LastModtime() // 获取文件最后一次修改时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastModtime Failed!!" << std::endl;return -1;}return st.st_mtim.tv_sec;}time_t LastAcctime() // 获取文件最后一次访问时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastAcctime Failed!!" << std::endl;return -1;}return st.st_atim.tv_sec;}std::string FileName() // 获取文件路径中的纯文件名称 a/b/c/test.cc --> test.cc{size_t pos=_filename.find_last_of("/");//寻找最后一个/if(pos==std::string::npos)//没找到,说明没有/{return _filename;}return _filename.substr(pos+1);//从pos截取到末尾}bool GetPosLen(std::string* body, size_t pos, size_t len){std::ifstream ifs;ifs.open(_filename, std::ios::binary);//打开文件,以二进制方式来读取数据if (ifs.is_open() == false)//打开失败{std::cout << "GetPosLen: open file failed!" << std::endl;return false;}size_t fsize = this->FileSize();//获取文件大小if (pos + len > fsize)//超过文件大小了{std::cout << "GetPosLen: get file len error" << 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 (this->GetContent(&body) == false)//源文件数据都存储在body里面{//获取源文件数据失败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 (this->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());//添加文件//注意迭代器返回的p不是string,不能直接将p添加进array里面,我们需要使用path将其}return true;}private:std::string _filename; // 文件名--包含路径};
};
cloud.cc
#include"util.hpp"
void FileUtilTest(const std::string &filename)
{cloud::FileUtil fu(filename);fu.CreateDirectory();std::vector<std::string>arry;fu.ScanDirectory(&arry);for(auto&a:arry){std::cout<<a<<std::endl;}
}
int main(int argc,char*argv[])
{FileUtilTest(argv[1]);return 0;
}
注意我们这个是使用了c++17里面的文件系统,这是需要我们额外链接的!
makefile
cloud:cloud.cc util.hpp bundle.cppg++ $^ -o $@ -lpthread -lstdc++fs
.PHONY:clean
clean:rm -f cloud
编译运行
发现它创建了一个目录
这个文件管理写的很好了吧!!
还是老样子!git push一下
3.JSON实用工具类实现
Jsoncpp
已经为我们你提供了序列化与反序列化接口,但是为了使得实用更加便捷,我们可以自己再封装一个JsonUtil
的类。
JsonUtil类中包含以下成员
class JsonUtil
{
public://这里使用static,是为了方便我们直接调用即可static bool Serialize(const Json::Value &root, std::string *str); // 序列化操作static bool Unserialize(const std::string &str, Json::Value *root); // 反序列化操作
};
由于前面的章节已经介绍过Json的使用,接下来我们直接看函数的实现。
class JsonUtil {
public:/*** @brief 将 Json::Value 对象序列化为字符串* @param root 输入的 JSON 数据结构(待序列化)* @param str 输出的序列化后的字符串* @return true 序列化成功,false 序列化失败*/static bool Serialize(const Json::Value &root, std::string *str) {// 1. 创建 JSON 流写入器构建器(可配置格式化选项,如缩进)Json::StreamWriterBuilder swb;// 2. 通过构建器生成 StreamWriter 对象(unique_ptr 自动管理内存)std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss; // 用于存储序列化结果// 3. 将 JSON 数据写入流// 返回值 0 表示成功,非 0 表示失败(JsonCpp 的约定)if (sw->write(root, &ss) != 0) {std::cout << "JSON 序列化失败!" << std::endl;return false;}// 4. 将 stringstream 内容转为字符串*str = ss.str();return true;}/*** @brief 将字符串反序列化为 Json::Value 对象* @param str 输入的 JSON 格式字符串* @param root 输出的解析后的 JSON 数据结构* @return true 解析成功,false 解析失败*/static bool Unserialize(const std::string &str, Json::Value *root) {// 1. 创建 JSON 字符读取器构建器Json::CharReaderBuilder crb;// 2. 通过构建器生成 CharReader 对象(unique_ptr 自动管理内存)std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err; // 存储解析错误信息// 3. 解析字符串// 参数说明:// - str.c_str():字符串起始地址// - str.c_str() + str.size():字符串结束地址// - root:输出解析后的 JSON 对象// - &err:错误信息输出bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);if (!ret) {std::cout << "JSON 解析错误: " << err << std::endl;return false;}return true;}
};
接下来来测试一下
makefile
cloud:cloud.cc util.hpp bundle.cppg++ $^ -o $@ -lpthread -lstdc++fs -ljsoncpp
.PHONY:clean
clean:rm -f cloud
cloud.cc
#include"util.hpp"
void JsonUtilTest()
{// 定义并初始化一个常量字符指针,指向字符串"小明"// 定义一个整型变量并初始化为18,表示年龄int age = 18;// 定义一个浮点型数组,存储三门课程的成绩float score[] = {77.5, 88, 93.6};// 定义一个Json::Value对象,作为JSON数据的根节点Json::Value root;// 向root中添加一个键值对,键为"name",值为name所指向的字符串root["name"] ="xiaoming";// 向root中添加一个键值对,键为"age",值为整型变量ageroot["age"] = age;// 向root中添加一个键为"成绩"的数组,并依次添加score数组中的元素// 使用append函数向数组中插入数据root["chengji"].append(score[0]);root["chengji"].append(score[1]);root["chengji"].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["name"].asString()<<std::endl;std::cout<<val["age"].asInt()<<std::endl;for(int i=0;i<val["chengji"].size();i++){std::cout<<val["chengji"][i].asFloat()<<std::endl;}
}
int main(int argc,char*argv[])
{JsonUtilTest();return 0;
}
我们编译运行一下
很完美
好了,我们git push一下即可
4.util.hpp源代码
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include"bundle.h"
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>namespace cloud {//注意:我们下面这些接口的名称可能会与系统的那些接口的名字重复,所以最好创建名称空间来避免名称污染namespace fs = std::experimental::filesystem;class FileUtil{public:FileUtil(const std::string& filename):_filename(filename){}//主要针对普通文件的接口int64_t FileSize() // 获取文件大小,这里使用64位的有符号的int,防止文件过大导致文件大小显示异常{struct stat st;int re=stat(_filename.c_str(),&st);if(re<0)//stat函数获取文件属性失败了{std::cout<<"Get FileSize Failed!!"<<std::endl;return -1;}return st.st_size;}time_t LastModtime() // 获取文件最后一次修改时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastModtime Failed!!" << std::endl;return -1;}return st.st_mtim.tv_sec;}time_t LastAcctime() // 获取文件最后一次访问时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastAcctime Failed!!" << std::endl;return -1;}return st.st_atim.tv_sec;}std::string FileName() // 获取文件路径中的纯文件名称 a/b/c/test.cc --> test.cc{size_t pos=_filename.find_last_of("/");//寻找最后一个/if(pos==std::string::npos)//没找到,说明没有/{return _filename;}return _filename.substr(pos+1);//从pos截取到末尾}bool GetPosLen(std::string* body, size_t pos, size_t len){std::ifstream ifs;ifs.open(_filename, std::ios::binary);//打开文件,以二进制方式来读取数据if (ifs.is_open() == false)//打开失败{std::cout << "GetPosLen: open file failed!" << std::endl;return false;}size_t fsize = this->FileSize();//获取文件大小if (pos + len > fsize)//超过文件大小了{std::cout << "GetPosLen: get file len error" << 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 (this->GetContent(&body) == false)//源文件数据都存储在body里面{//获取源文件数据失败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 (this->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());//添加文件//注意迭代器返回的p不是string,不能直接将p添加进array里面,我们需要使用path将其}return true;}private:std::string _filename; // 文件名--包含路径};class JsonUtil {public:/*** @brief 将 Json::Value 对象序列化为字符串* @param root 输入的 JSON 数据结构(待序列化)* @param str 输出的序列化后的字符串* @return true 序列化成功,false 序列化失败*/static bool Serialize(const Json::Value &root, std::string *str) {// 1. 创建 JSON 流写入器构建器(可配置格式化选项,如缩进)Json::StreamWriterBuilder swb;// 2. 通过构建器生成 StreamWriter 对象(unique_ptr 自动管理内存)std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss; // 用于存储序列化结果// 3. 将 JSON 数据写入流// 返回值 0 表示成功,非 0 表示失败(JsonCpp 的约定)if (sw->write(root, &ss) != 0) {std::cout << "JSON 序列化失败!" << std::endl;return false;}// 4. 将 stringstream 内容转为字符串*str = ss.str();return true;}/*** @brief 将字符串反序列化为 Json::Value 对象* @param str 输入的 JSON 格式字符串* @param root 输出的解析后的 JSON 数据结构* @return true 解析成功,false 解析失败*/static bool Unserialize(const std::string &str, Json::Value *root) {// 1. 创建 JSON 字符读取器构建器Json::CharReaderBuilder crb;// 2. 通过构建器生成 CharReader 对象(unique_ptr 自动管理内存)std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err; // 存储解析错误信息// 3. 解析字符串// 参数说明:// - str.c_str():字符串起始地址// - str.c_str() + str.size():字符串结束地址// - root:输出解析后的 JSON 对象// - &err:错误信息输出bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);if (!ret) {std::cout << "JSON 解析错误: " << err << std::endl;return false;}return true;}};
};
相关文章:
【云备份】服务端工具类实现
1.文件实用工具类设计 不管是客户端还是服务端,文件的传输备份都涉及到文件的读写,包括数据管理信息的持久化也是如此,因此首先设 计封装文件操作类,这个类封装完毕之后,则在任意模块中对文件进行操作时都将变的简单化…...
如何在Cursor中使用MCP服务
前言 随着AI编程助手的普及,越来越多开发者选择在Cursor等智能IDE中进行高效开发。Cursor不仅支持代码补全、智能搜索,还能通过MCP(Multi-Cloud Platform)服务,轻松调用如高德地图API、数据库等多种外部服务ÿ…...
PB的框架advgui反编译后控件无法绘制的处理(即导入pbx的操作步骤)
advguiobjects.pbl反编译后,涉及到里面一个用pbni开发的一个绘制对象需要重新导入才可以。否则是黑色的无法绘制控件: 对象的位置在: 操作: 导入pbx文件中的对象。 恢复正常: 文章来源:PB的框架advgui反编译…...
第 11 届蓝桥杯 C++ 青少组中 / 高级组省赛 2020 年真题,选择题详细解释
一、选择题 第 2 题 在二维数组按行优先存储的情况下,元素 a[i][j] 前的元素个数计算如下: 1. **前面的完整行**:共有 i 行,每行 n 个元素,总计 i * n 个元素。 2. **当前行的前面元素**:在行内&#x…...
Python 装饰器基础知识科普
装饰器定义与基本原理 装饰器本质上是一个可调用的对象,它接收另一个函数(即被装饰的函数)作为参数。装饰器可以对被装饰的函数进行处理,之后返回该函数,也可以将其替换为另一个函数或可调用对象。 代码示例理解 有…...
数字基带信号和频带信号的区别解析
数字基带信号和数字频带信号是通信系统中两种不同的信号形式,它们的核心区别在于是否经过调制以及适用的传输场景。以下是两者的主要区别和分析: 1. 定义与核心区别 数字基带信号(Digital Baseband Signal) 未经调制的原始数字信号…...
Nginx Proxy Manager 中文版安装部署
目录 Nginx Proxy Manager 中文版安装部署教程一、项目简介1.1 主要功能特点1.2 项目地址1.3 系统架构与工作原理1.4 适用场景 二、系统要求2.1 硬件要求2.2 软件要求 三、Docker环境部署3.1 CentOS系统安装Docker3.2 Ubuntu系统安装Docker3.3 安装Docker Compose 四、安装Ngin…...
类和对象(拷贝构造和运算符重载)下
类和对象(拷贝构造和运算符重载)下 这一集的主要目标先是接着上一集讲完日期类。然后再讲一些别的运算符的重载,和一些语法点。 这里我把这一集要用的代码先放出来:(大家拷一份代码放在编译器上先) Date.h #include <iostream> #include <cassert> …...
Codeforces Round 1008 (Div. 2) C
C 构造 题意:a的数据范围大,b的数据范围小,要求所有的a不同,考虑让丢失的那个a最大即可。问题变成:构造一个最大的a[i] 思路:令a2是最大的,将a1,a3,a5....a2*n1,置为最大的b,将a4,a…...
操作系统(1)多线程
在当今计算机科学领域,多线程技术已成为提高程序性能和响应能力的关键手段。无论是高性能计算、Web服务器还是图形用户界面应用程序,多线程都发挥着不可替代的作用。本文将全面介绍操作系统多线程的概念、实现原理、同步机制以及实际应用场景,…...
系统架构设计师:设计模式——创建型设计模式
一、创建型设计模式 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建、组合和表示它的那些对象。一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象。 随着系统演化得越来越依赖于对象复合而不是类…...
使用Set和Map解题思路
前言 Set和Map这两种数据结构,在解决一些题上,效率很高。跟大家简单分享一些题以及如何使用Set和Map去解决这些题目。 题目链接 136. 只出现一次的数字 - 力扣(LeetCode) 138. 随机链表的复制 - 力扣(LeetCode) 旧…...
Java 算法入门:从基础概念到实战示例
在计算机科学领域,算法如同魔法咒语,能够将无序的数据转化为有价值的信息。对于 Java 开发者而言,掌握算法不仅是提升编程能力的关键,更是解决复杂问题的核心武器。本文将带领你走进 Java 算法的世界,从基础概念入手&a…...
【大模型】图像生成:ESRGAN:增强型超分辨率生成对抗网络的革命性突破
深度解析ESRGAN:增强型超分辨率生成对抗网络的革命性突破 技术演进与架构创新核心改进亮点 环境配置与快速入门硬件要求安装步骤 实战全流程解析1. 单图像超分辨率重建2. 自定义数据集训练3. 视频超分处理 核心技术深度解析1. 残差密集块(RRDB࿰…...
记录搭建自己的应用中心-需求看板搭建
记录搭建自己的应用中心-需求看板搭建 人员管理新增用户组织用户登录和操作看板状态看板任务通知任务详情 人员管理 由于不是所有人都有应用管理权限,所以额外做了一套应用登录权限,做了一个新的组织人员表,一个登录账户下的所有应用人员共享…...
探秘数据结构:构建高效算法的灵魂密码
摘要 数据结构作为计算机科学的基石,其设计与优化直接影响算法效率、资源利用和系统可靠性。本文系统阐述数据结构的基础理论、分类及其核心操作,涵盖数组、链表、栈、队列、树、图、哈希表与堆等经典类型。深入探讨各结构的应用场景与性能对比…...
多节点监测任务分配方法比较与分析
多监测节点任务分配方法是分布式系统、物联网(IoT)、工业监测等领域的核心技术,其核心目标是在资源受限条件下高效分配任务,以优化系统性能。以下从方法分类、对比分析、应用场景选择及挑战等方面进行系统阐述: 图1 多…...
spring-boot-maven-plugin 将spring打包成单个jar的工作原理
spring-boot-maven-plugin 是 Spring Boot 的 Maven 插件,它的核心功能是将 Spring Boot 项目打包成一个独立的、可执行的 Fat JAR(包含所有依赖的 JAR 包)。以下是它的工作原理详解: 1. 默认 Maven 打包 vs Spring Boot 插件打包…...
盐化行业数字化转型规划详细方案(124页PPT)(文末有下载方式)
资料解读:《盐化行业数字化转型规划详细解决方案》 详细资料请看本解读文章的最后内容。 该文档聚焦盐化行业数字化转型,全面阐述了盐化企业信息化建设的规划方案,涵盖战略、架构、实施计划、风险及效益等多个方面,旨在通过数字化…...
开源革命:从技术共享到产业变革——卓伊凡的开源实践与思考-优雅草卓伊凡
开源革命:从技术共享到产业变革——卓伊凡的开源实践与思考-优雅草卓伊凡 一、开源的本质与行业意义 1.1 开源软件的定义与内涵 当卓伊凡被问及”软件开源是什么”时,他给出了一个生动的比喻:”开源就像将食谱公之于众的面包师,…...
解锁 C++26 的未来:从语言标准演进到实战突破
一、C26 的战略定位与开发进展 C26 的开发已进入功能冻结阶段,预计 2026 年正式发布。作为 C 标准委员会三年一迭代的重要版本,其核心改进聚焦于并发与并行性的深度优化,同时在内存管理、元编程等领域实现重大突破。根据 ISO C 委员会主席 H…...
terraform实现本地加密与解密
在 Terraform 中实现本地加密与解密(不依赖云服务),可以通过 OpenSSL 或 GPG 等本地加密工具配合 External Provider 实现。以下是完整的安全实现方案: 一、基础架构设计 # 文件结构 . ├── secrets │ ├── encrypt.sh …...
黄雀在后:外卖大战新变局,淘宝+饿了么开启电商大零售时代
当所有人以为美团和京东的“口水战”硝烟渐散,外卖大战告一段落时,“螳螂捕蝉,黄雀在后”,淘宝闪购联合饿了么“闪现”外卖战场,外卖烽火再度燃起。 4 月30日,淘宝天猫旗下即时零售业务“小时达”正式升级…...
基本功能学习
一.enum枚举使用 E_SENSOR_REQ_NONE 的定义及用途 在传感器驱动开发或者电源管理模块中,E_SENSOR_REQ_NONE通常被用来表示一种特殊的状态或请求模式。这种状态可能用于指示当前没有活动的传感器请求,或者是默认初始化状态下的一种占位符。 可能的定义…...
59常用控件_QComboBox的使用
目录 代码示例:使用下拉框模拟麦当劳点餐 代码示例:从文件中加载下拉框的选项 QComboBox表示下拉框 核心属性 属性说明currentText当前选中的文本currentIndex当前选中的条目下标。 从 0 开始计算。如果当前没有条目被选中,值为 -1editable是否允许修改…...
卡洛诗西餐的文化破圈之路
在餐饮市场的版图上,西餐曾长期被贴上“高端”“舶来品”“纪念日专属”的标签,直到卡洛诗以高性价比西餐的定位破局,将意大利风情与家庭餐桌无缝衔接。这场从异国符号到家常选择的转型,不仅是商业模式的创新,更是一部…...
Python-57:Base32编码和解码问题
问题描述 你需要实现一个 Base32 的编码和解码函数。 相比于 Base32,你可能更熟悉 Base64,Base64 是非常常见的用字符串形式表示二进制数据的方式,在邮件附件、Web 中的图片中都有广泛的应用。 Base32 是 Base64 的变种,与 Bas…...
【排序算法】八大经典排序算法详解
一、直接选择排序(Selection Sort)算法思想算法步骤特性分析 二、堆排序(Heap Sort)算法思想关键步骤特性分析 三、直接插入排序(Insertion Sort)算法思想算法步骤特性分析 四、希尔排序(Shell …...
近端策略优化PPO详解:python从零实现
🧠 向所有学习者致敬! “学习不是装满一桶水,而是点燃一把火。” —— 叶芝 我的博客主页: https://lizheng.blog.csdn.net 🌐 欢迎点击加入AI人工智能社区! 🚀 让我们一起努力,共创…...
C# System.Text.Json终极指南(十):从基础到高性能序列化实战
一、JSON序列化革命:System.Text.Json的架构优势 1.1 核心组件解析 1.2 性能基准测试(.NET 8) 操作Newtonsoft.JsonSystem.Text.Json性能提升简单对象序列化1,200 ns450 ns2.7x大型对象反序列化15 ms5.2 ms2.9x内存分配(1k次操作)45 MB12 MB3.75x二、基础序列化操作精解 …...
Centos7.9 安装mysql5.7
1.配置镜像(7.9的镜像过期了) 2.备份原有的 CentOS 基础源配置文件 sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak 3.更换为国内镜像源 sudo vi /etc/yum.repos.d/CentOS-Base.repo 将文件内容替换为以下内容&am…...
Qt指南针
Qt写的指南针demo. 运行结果 滑动调整指针角度 实现代码 h文件 #ifndef COMPASS_H #define COMPASS_H#include <QWidget> #include <QColor>class Compass : public QWidget {Q_OBJECT// 可自定义属性Q_PROPERTY(QColor backgroundColor READ backgroundColor WRI…...
杜邦分析法
杜邦分析法(DuPont Analysis)是一种用于分析企业财务状况和经营绩效的综合分析方法,由美国杜邦公司在20世纪20年代率先采用,故得名。以下是其相关内容介绍: 核心指标与分解 净资产收益率(ROE):杜邦分析法的核心指标,反映股东权益的收益水平,用以衡量公司运用自有资本…...
给U盘加上图标
电脑插入U盘后,U盘的那个标志没有特色,我们可以换成有意义的照片作为U盘图标,插上U盘就能看到,多么地浪漫。那该如何设置呢?一起来看看吧 选择一张ICO格式的图片到U盘里 PNG转ICO - 在线转换图标文件PNG转ICO - 免费…...
人工智能对未来工作的影响
人工智能对未来工作的影响是多方面的,既包括对就业结构的改变,也涉及工作方式、职业技能需求以及社会政策的调整。以下是对人工智能对未来工作影响的详细分析: 一、就业结构的变革 岗位替代与消失 人工智能技术在许多领域展现出强大的自动化…...
RocketMQ常见面试题一
1. RocketMQ 是什么?它的核心组件有哪些? 答:RocketMQ 是阿里巴巴开源的一款分布式消息中间件,支持高吞吐、低延迟、高可用的消息发布与订阅。 核心组件: NameServer:轻量级注册中心,管理 Broker 的元数据(路由信息),无状态。 Broker:消息存储和转发节点,分为 Mas…...
C++调试(壹):初步认识WinDbg和dump文件
目录 1.前言 2.WinDbg是什么? 3.WinDbg安装 4.Dump文件是什么? 5.生成Dump文件的场景 前言 这是一个关于C调试的博客,该系列博客主要是讲解如何使用WinDbg工具结合dump文件调试程序。在日常开发过程中,我们往往无法完…...
centos7 离线安装python3 保留python2
一、事前准备: (1)查看centos具体版本 cat /etc/redhat-releaseCentOS Linux release 7.4.1708 (Core) (2)查看linux中当前python版本 centos7 默认安装python2.7.5 (3)查看python3的依赖&#…...
【dify—9】Chatflow实战——博客文章生成器
目录 一、创建Chatflow 二、创建变量 三、添加时间工具 四、编写提示词 五、回复输出 六、运行 第一部分 安装difydocker教程:【difydocker安装教程】-CSDN博客 第二部分 dock重装教程:【dify—2】docker重装-CSDN博客 第三部分 dify拉取镜像&…...
华为OD机试真题——斗地主之顺子(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录…...
3.2/Q2,Charls最新文章解读
文章题目:Internet usage elevates elderly obesity: evidence from a difference-in-differences analysis of the broadband China policy DOI:10.1186/s13690-025-01565-9 中文标题:互联网使用导致老年人肥胖:中国宽带政策差异…...
Seata服务端开启事务核心源码解析
文章目录 概述一、doGlobalBegin1.1、createGlobalSession1.2、addSessionLifecycleListener1.3、begin 概述 Seata服务端作为TC角色,用于接收客户端标注了GlobalTransactional也就是TM角色的开启事务,提交/回滚事务请求,维护全局和分支事务的…...
Seata服务端回滚事务核心源码解析
文章目录 前言一、doGlobalRollback3.1、changeGlobalStatus3.2、doGlobalRollback 前言 本篇介绍Seata服务端接收到客户端TM回滚请求,进行处理并且驱动所有的RM进行回滚的源码。 一、doGlobalRollback doGlobalRollback是全局回滚的方法: 首先依旧…...
PMP-第九章 项目资源管理(一)
项目资源管理概述 项目资源管理包括识别、获取和管理所需资源以完成项目的各个过程资源主要分为实物资源和人力资源;实物资源包括设备、材料和基础设施等团队资源或人员指的是人力资源团队资源管理与项目干系人管理有重叠的部分,本章重点关注组成项目团…...
【Unity】MVP框架的使用例子
在提到MVP之前,可以先看看这篇MVC的帖子: 【Unity】MVC的简单分享以及一个在UI中使用的例子 MVC的不足之处: 在MVC的使用中,会发现View层直接调用了Model层的引用,即这两个层之间存在着一定的耦合性,而MV…...
Matlab/Simulink - BLDC直流无刷电机仿真基础教程(四) - PWM调制模拟
Matlab/Simulink - BLDC直流无刷电机仿真基础教程(四) - PWM调制模拟 前言一、PWM调制技术基本原理二、仿真模型中加入PWM调制三、逆变电路MOS管添加体二极管四、模拟添加机械负载五、仿真模型与控制框图文章相关模型文件下载链接参考链接 前言 本系列文…...
x86架构详解:定义、应用及特点
一、x86架构的定义 x86 是由Intel公司开发的复杂指令集(CISC)处理器架构,起源于1978年的Intel 8086处理器,后续扩展至32位(IA-32)和64位(x86-64)。其名称来源于早期处理器型号的“8…...
C++学习-入门到精通-【3】控制语句、赋值、自增和自减运算符
C学习-入门到精通-【3】控制语句、赋值、自增和自减运算符 控制语句、赋值、自增和自减运算符 C学习-入门到精通-【3】控制语句、赋值、自增和自减运算符一、什么是算法二、伪代码三、控制结构顺序结构选择结构if语句if...else语句switch语句 循环结构while语句 四、算法详述&a…...
【Bootstrap V4系列】学习入门教程之 页面内容排版
Bootstrap V4 学习入门教程之 页面内容排版 按钮上的指针排版一、Global settings 全局设置二、Headings 标题2.1 Customizing headings 自定义标题2.2 Display headings 显示标题2.3 Lead 引导 三、Blockquotes 块引用3.1 Naming a source 命名源3.2 Alignment 对齐 四、Lists…...
GTA5(传承/增强) 13980+真车 超跑 大型载具MOD整合包+最新GTA6大型地图MOD 5月最新更新
1500超跑载具 1000普通超跑 1500真车超跑 各种军载具1000 各种普通跑车 船舶 飞机 1000 人物1500 添加式led载具1000 超级英雄最新版 添加添加式武器MOD1000 添加地图MOD500 添加超跑载具2000 当前共计1.2wMOD 4月2日更新 新增770menyoo地图 当前共计12770 新增48款超级英雄最新…...