1. 关键词
C++ 文件系统操作 拷贝文件 拷贝文件夹 跨平台
2. fileutil.h
| |
| #pragma once |
| |
| #include <string> |
| #include <cstdio> |
| #include <cstdint> |
| #include "filetype.h" |
| #include "filepath.h" |
| |
| namespace cutl |
| { |
| |
| |
| |
| |
| |
| class file_guard |
| { |
| public: |
| |
| |
| |
| |
| |
| explicit file_guard(FILE *file); |
| |
| |
| |
| |
| |
| ~file_guard(); |
| |
| |
| |
| |
| |
| |
| FILE *getfd() const; |
| |
| private: |
| FILE *file_; |
| }; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes = false); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool copydir(const filepath &srcdir, const filepath &dstdir); |
| |
| } |
3. fileutil.cpp
| #include <cstdio> |
| #include <map> |
| #include <iostream> |
| #include <cstring> |
| #include <sys/stat.h> |
| #include "fileutil.h" |
| #include "inner/logger.h" |
| #include "inner/filesystem.h" |
| #include "strutil.h" |
| |
| namespace cutl |
| { |
| file_guard::file_guard(FILE *file) |
| : file_(file) |
| { |
| } |
| |
| file_guard::~file_guard() |
| { |
| if (file_) |
| { |
| |
| int ret = fclose(file_); |
| if (ret != 0) |
| { |
| CUTL_ERROR("fail to close file, ret" + std::to_string(ret)); |
| } |
| file_ = nullptr; |
| } |
| |
| } |
| |
| FILE *file_guard::getfd() const |
| { |
| return file_; |
| } |
| |
| bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes) |
| { |
| |
| |
| |
| if (srcpath.isfile()) |
| { |
| if (dstpath.exists()) |
| { |
| |
| removefile(dstpath); |
| } |
| file_guard frd(fopen(srcpath.str().c_str(), "rb")); |
| if (frd.getfd() == nullptr) |
| { |
| CUTL_ERROR("open file failed, " + srcpath.str()); |
| return false; |
| } |
| file_guard fwt(fopen(dstpath.str().c_str(), "wb")); |
| if (fwt.getfd() == nullptr) |
| { |
| CUTL_ERROR("open file failed, " + dstpath.str()); |
| return false; |
| } |
| |
| static constexpr size_t buf_size = 8 * 1024; |
| uint8_t buffer[buf_size] = {0}; |
| size_t read_len = 0; |
| size_t write_len = 0; |
| while ((read_len = fread(buffer, 1, buf_size, frd.getfd())) > 0) |
| { |
| write_len = fwrite(buffer, 1, read_len, fwt.getfd()); |
| if (write_len != read_len) |
| { |
| CUTL_ERROR("write file failed, only write " + std::to_string(write_len) + ", read_len:" + std::to_string(read_len)); |
| return false; |
| } |
| } |
| |
| int ret = fflush(fwt.getfd()); |
| if (0 != ret) |
| { |
| CUTL_ERROR("fail to flush file:" + dstpath.str()); |
| return false; |
| } |
| if (!file_sync(fwt.getfd())) |
| { |
| CUTL_ERROR("file_sync failed for " + dstpath.str()); |
| return false; |
| } |
| } |
| else if (srcpath.issymlink()) |
| { |
| if (dstpath.exists()) |
| { |
| |
| file_removelink(dstpath.str()); |
| } |
| auto link_path = file_readlink(srcpath.str()); |
| if (link_path.empty()) |
| { |
| CUTL_ERROR("readlink failed for " + srcpath.str()); |
| return false; |
| } |
| if (!file_createlink(link_path, dstpath.str())) |
| { |
| CUTL_ERROR("createlink failed for " + dstpath.str()); |
| return false; |
| } |
| } |
| else |
| { |
| CUTL_ERROR("not a file or symlink, cannot copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str()); |
| return false; |
| } |
| |
| |
| if (attributes && srcpath.isfile()) |
| { |
| return copy_attributes(srcpath.str(), dstpath.str()); |
| } |
| |
| return true; |
| } |
| |
| |
| bool copydir(const filepath &srcdir, const filepath &dstdir) |
| { |
| if (!srcdir.isdir()) |
| { |
| CUTL_ERROR("srcdir is not a directory: " + srcdir.str()); |
| return false; |
| } |
| |
| if (!dstdir.exists() && !createdir(dstdir, true)) |
| { |
| CUTL_ERROR("createdir failed for " + dstdir.str()); |
| return false; |
| } |
| |
| auto filelist = list_files(srcdir, filetype::all, true); |
| for (size_t i = 0; i < filelist.size(); i++) |
| { |
| auto file = filelist[i]; |
| auto src_file = file.filepath; |
| auto reletive_path = src_file.substr(srcdir.str().length() + 1); |
| auto dstpath = dstdir.join(reletive_path); |
| auto srcpath = cutl::path(src_file); |
| if (file.type == filetype::file || file.type == filetype::symlink) |
| { |
| if (!copyfile(srcpath, dstpath, true)) |
| { |
| return false; |
| } |
| } |
| else if (file.type == filetype::directory) |
| { |
| if (!createdir(dstpath, true)) |
| { |
| return false; |
| } |
| |
| if (!copy_attributes(src_file, dstpath.str(), true)) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| CUTL_WARN("the file cannot be copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str()); |
| continue; |
| } |
| } |
| |
| return true; |
| } |
4. filesystem_win.h
| |
| #include <vector> |
| #include <string> |
| |
| #pragma once |
| |
| namespace cutl |
| { |
| |
| bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir = false); |
| } |
5. filesystem_win.cpp
| #if defined(_WIN32) || defined(__WIN32__) |
| |
| #include <io.h> |
| #include <direct.h> |
| #include <Windows.h> |
| #include <stdlib.h> |
| #include "strutil.h" |
| #include "filesystem.h" |
| #include "logger.h" |
| |
| namespace cutl |
| { |
| bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir) |
| { |
| |
| FILETIME t_create, t_access, t_write; |
| HANDLE h_src = NULL; |
| if (isdir) |
| { |
| |
| h_src = CreateFile( |
| srcpath.c_str(), |
| GENERIC_READ, |
| FILE_SHARE_READ | FILE_SHARE_DELETE, |
| NULL, |
| OPEN_EXISTING, |
| FILE_FLAG_BACKUP_SEMANTICS, |
| NULL); |
| } |
| else |
| { |
| h_src = CreateFileA( |
| srcpath.c_str(), |
| GENERIC_READ, |
| 0, |
| NULL, |
| OPEN_EXISTING, |
| FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, |
| NULL); |
| } |
| |
| if (h_src == INVALID_HANDLE_VALUE) |
| { |
| CUTL_ERROR("Failed to open file " + srcpath + ", error code: " + std::to_string(GetLastError())); |
| CloseHandle(h_src); |
| return false; |
| } |
| if (!GetFileTime(h_src, &t_create, &t_access, &t_write)) |
| { |
| CUTL_ERROR("Failed to get file times for " + srcpath + ", error code: " + std::to_string(GetLastError())); |
| CloseHandle(h_src); |
| return false; |
| } |
| CloseHandle(h_src); |
| |
| |
| HANDLE h_dst = NULL; |
| if (isdir) |
| { |
| h_dst = CreateFile( |
| dstpath.c_str(), |
| GENERIC_READ | GENERIC_WRITE, |
| FILE_SHARE_READ | FILE_SHARE_DELETE, |
| NULL, |
| OPEN_EXISTING, |
| FILE_FLAG_BACKUP_SEMANTICS, |
| NULL); |
| } |
| else |
| { |
| h_dst = ::CreateFileA( |
| dstpath.c_str(), |
| |
| |
| GENERIC_ALL, |
| 0, |
| NULL, |
| OPEN_EXISTING, |
| FILE_ATTRIBUTE_NORMAL, |
| NULL); |
| } |
| |
| if (h_dst == INVALID_HANDLE_VALUE) |
| { |
| CUTL_ERROR("Failed to open file " + dstpath + ", error code: " + std::to_string(GetLastError())); |
| CloseHandle(h_dst); |
| return false; |
| } |
| if (!SetFileTime(h_dst, &t_create, &t_access, &t_write)) |
| { |
| CUTL_ERROR("Failed to set file times for " + dstpath + ", error code: " + std::to_string(GetLastError())); |
| CloseHandle(h_dst); |
| return false; |
| } |
| CloseHandle(h_dst); |
| |
| |
| |
| DWORD attributes = GetFileAttributesA(srcpath.c_str()); |
| if (attributes == INVALID_FILE_ATTRIBUTES) |
| { |
| CUTL_ERROR("Failed to get file attributes for " + srcpath + ", error code: " + std::to_string(GetLastError())); |
| return false; |
| } |
| if (!SetFileAttributesA(dstpath.c_str(), attributes)) |
| { |
| CUTL_ERROR("Failed to set file attributes for " + dstpath + ", error code: " + std::to_string(GetLastError())); |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| #endif |
6. filesystem_unix.cpp
| #if defined(_WIN32) || defined(__WIN32__) |
| |
| #else |
| |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <dirent.h> |
| #include <stack> |
| #include <cstring> |
| #include <utime.h> |
| #include <stdlib.h> |
| #include <sys/time.h> |
| #include "filesystem.h" |
| #include "inner/logger.h" |
| |
| namespace cutl |
| { |
| bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir) |
| { |
| struct stat attr_of_src; |
| int ret = lstat(srcpath.c_str(), &attr_of_src); |
| if (ret != 0) |
| { |
| CUTL_ERROR("lstat error. srcpath:" + srcpath + ", error:" + strerror(errno)); |
| return false; |
| } |
| |
| |
| ret = chmod(dstpath.c_str(), attr_of_src.st_mode); |
| if (ret != 0) |
| { |
| CUTL_ERROR("chmod error. dstpath:" + dstpath + ", error:" + strerror(errno)); |
| return false; |
| } |
| |
| ret = chown(dstpath.c_str(), attr_of_src.st_uid, attr_of_src.st_gid); |
| if (ret != 0) |
| { |
| CUTL_ERROR("chown error. dstpath:" + dstpath + ", error:" + strerror(errno)); |
| return false; |
| } |
| |
| |
| if (S_ISLNK(attr_of_src.st_mode)) |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| else |
| { |
| struct utimbuf tbuf; |
| tbuf.actime = attr_of_src.st_atime; |
| tbuf.modtime = attr_of_src.st_mtime; |
| ret = utime(dstpath.c_str(), &tbuf); |
| if (ret != 0) |
| { |
| CUTL_ERROR("utime error. dstpath:" + dstpath + ", error:" + strerror(errno)); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| #endif |
7. 源码地址
更多详细代码,请查看本人写的C++ 通用工具库: common_util, 本项目已开源,代码简洁,且有详细的文档和Demo。
【SunLogging】
扫码二维码,关注微信公众号,阅读更多精彩内容
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战