C++文件系统操作6

cnblogs 2024-07-27 08:09:02 阅读 76

  • 1. 关键词
  • 2. fileutil.h
  • 3. fileutil.cpp
  • 4. filesystem_win.h
  • 5. filesystem_win.cpp
  • 6. filesystem_unix.cpp
  • 7. 源码地址

1. 关键词

C++ 文件系统操作 拷贝文件 拷贝文件夹 跨平台

2. fileutil.h

<code>

#pragma once

#include <string>

#include <cstdio>

#include <cstdint>

#include "filetype.h"

#include "filepath.h"

namespace cutl

{

/**

* @brief The file guard class to manage the FILE pointer automatically.

* file_guard object can close the FILE pointer automatically when his scope is exit.

*/

class file_guard

{

public:

/**

* @brief Construct a new file guard object

*

* @param file the pointer of the FILE object

*/

explicit file_guard(FILE *file);

/**

* @brief Destroy the file guard object

*

*/

~file_guard();

/**

* @brief Get the FILE pointer.

*

* @return FILE*

*/

FILE *getfd() const;

private:

FILE *file_;

};

/**

* @brief Copy a file or symbolic link(shortcut on windows).

*

* @param srcpath the filepath of the source file or symbolic link to be copied

* @param dstpath the filepath of the destination file or symbolic link to be copied to

* @param attributes whether to copy the file attributes, default is false.

* If true, means copy the file attributes, like the 'cp -p' command.

* @return true if the file or symbolic link is copied successfully, false otherwise.

* @note If the destination file or directory already exists, it will be overwritten.

* @return false

*/

bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes = false);

// copy directory recursively

/**

* @brief Copy a directory recursively.

*

* @param srcdir the filepath of the source directory to be copied

* @param dstdir the filepath of the destination directory to be copied to

* @return true if the directory is copied successfully, false otherwise.

* @note If the destination directory already exists, it will be overwritten.

*/

bool copydir(const filepath &srcdir, const filepath &dstdir);

} // namespace cutl

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_)

{

// CUTL_DEBUG("close file");

int ret = fclose(file_);

if (ret != 0)

{

CUTL_ERROR("fail to close file, ret" + std::to_string(ret));

}

file_ = nullptr;

}

// ROBOLOG_DCHECK(file_ == nullptr);

}

FILE *file_guard::getfd() const

{

return file_;

}

bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes)

{

// CUTL_INFO("file type: " + std::to_string(srcpath.type()) + ", " + filetype_flag(srcpath.type()) + ", " + srcpath.str() + ", dstpath:" + dstpath.str());

// copy file content

if (srcpath.isfile())

{

if (dstpath.exists())

{

// remove if 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;

}

}

// flush file to disk

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())

{

// remove if 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;

}

// copy file attributes

if (attributes && srcpath.isfile())

{

return copy_attributes(srcpath.str(), dstpath.str());

}

return true;

}

// https://www.cnblogs.com/harrypotterjackson/p/12113382.html

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

{

// 拷贝文件/文件夹的属性,isdir参数只在windows下生效,Unix-like系统下不起作用

bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir = false);

} // namespace cutl

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_WRITE,

// GENERIC_READ | GENERIC_WRITE,

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;

}

} // namespace cutl

#endif // defined(_WIN32) || defined(__WIN32__)

6. filesystem_unix.cpp

#if defined(_WIN32) || defined(__WIN32__)

// do nothing

#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))

{

// TODO: 编译还有问题,需要确定编译宏

// struct timeval time_buf[2];

// time_buf[0].tv_sec = attr_of_src.st_atim.tv_sec;

// time_buf[0].tv_usec = attr_of_src.st_atim.tv_nsec / 1000;

// time_buf[1].tv_sec = attr_of_src.st_mtim.tv_sec;

// time_buf[1].tv_usec = attr_of_src.st_mtim.tv_nsec / 1000;

// ret = lutimes(dstpath.c_str(), time_buf);

// if (ret != 0)

// {

// CUTL_ERROR("lutimes error. dstpath:" + dstpath + ", error:" + strerror(errno));

// return false;

// }

}

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;

}

} // namespace cutl

#endif // defined(_WIN32) || defined(__WIN32__)

7. 源码地址

更多详细代码,请查看本人写的C++ 通用工具库: common_util, 本项目已开源,代码简洁,且有详细的文档和Demo。


【SunLogging】

扫码二维码,关注微信公众号,阅读更多精彩内容



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。