Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

1. 关键词

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

2. fileutil.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#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

1
2
3
4
5
6
7
8
9
10
11

#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#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。

推荐阅读
C++文件系统操作1 - 跨平台实现文件的创建和删除 C++文件系统操作1 - 跨平台实现文件的创建和删除 C++文件系统操作5 - 跨平台列出指定目录下的所有文件和文件夹 C++文件系统操作5 - 跨平台列出指定目录下的所有文件和文件夹 C++文件系统操作4 - 跨平台实现获取文件|文件夹的大小 C++文件系统操作4 - 跨平台实现获取文件|文件夹的大小

评论