小文件复制用 std::ifstream/ofstream 流式读写最稳,需开二进制模式、设 8KB 缓冲区、用 gcount() 判断末次读取;大文件(>100MB)应优先用 std::filesystem::copy(C++17),并务必检查返回值和捕获 filesystem_error 异常。

用 std::ifstream 和 std::ofstream 复制小文件最稳
直接读完再写完,适合几 MB 以内的文件,逻辑清晰、出错好定位。关键不是“能不能”,而是「缓冲区怎么设」和「二进制模式要不要开」。
常见错误现象:std::ios::binary 没加,复制图片或可执行文件后损坏;用 operator 而不是 <code>read()/write(),遇到 \0 就截断。
- 源文件必须用
std::ios::binary打开,目标文件同理 - 别用
in >> buffer,改用in.read(buf, size)+out.write(buf, n) - 缓冲区大小建议设为 8192(8KB),太小频繁系统调用,太大无意义
- 每次
read()后检查gcount(),不是eof()——eof()只在读失败后才置位
std::ifstream in("src.bin", std::ios::binary);
std::ofstream out("dst.bin", std::ios::binary);
char buf[8192];
while (in.read(buf, sizeof(buf))) {
out.write(buf, sizeof(buf));
}
if (in.gcount() > 0) {
out.write(buf, in.gcount());
}
大文件复制要用 std::filesystem::copy(C++17)
超过 100MB 的文件,自己流式读写容易卡顿、占 CPU、且不利用系统优化。C++17 的 std::filesystem::copy 底层会走 copy_file_range(Linux)或 CopyFile(Windows),零拷贝或内核加速。
使用场景:构建工具、安装程序、备份逻辑中需要可靠、高效、跨平台的复制。
立即学习“C++免费学习笔记(深入)”;
- 必须包含
<filesystem>,链接时可能需加-lstdc++fs(GCC 旧版本) - 默认行为是覆盖目标文件,要跳过已存在文件得传
std::filesystem::copy_options::skip_existing - 不抛异常,但返回值是
bool,失败时静默 —— 务必检查返回值 - Windows 上路径含中文没问题,Linux 上注意 locale 是否支持 UTF-8 路径
namespace fs = std::filesystem;
bool ok = fs::copy("a.log", "b.log", fs::copy_options::overwrite_existing);
if (!ok) {
// 实际失败了,但没报错 —— 这里得自己处理
}
std::filesystem::copy 报 std::filesystem::filesystem_error 怎么办
它不总返回 false,某些条件(如权限不足、目标只读、路径过长)会直接抛异常。很多人只查返回值,漏捕获这个异常,导致程序崩溃。
典型错误信息:filesystem error: cannot copy: Permission denied 或 No such file or directory。
- 必须用
try/catch包住调用,不能只信返回值 - 异常对象有
.path1()和.path2()方法,能准确定位哪个路径出问题 - 不要 catch
std::exception&就完事 ——filesystem_error是独立类型,最好显式捕获 - 如果不想中断流程,可用带
std::error_code&的重载,它不抛异常,把错误码写进参数里
std::error_code ec;
fs::copy("src", "dst", fs::copy_options::overwrite_existing, ec);
if (ec) {
// ec.message() 是 "Permission denied" 这类字符串
// ec.value() 是 errno 值,比如 EACCES == 13
}
Windows 下用 CreateFile + CopyFile 更底层但更可控
当你要精确控制安全描述符、继承性、或绕过 std::filesystem 的 ABI 兼容问题(比如混用不同 MSVC 版本编译的库),就得调 Win32 API。
性能上和 std::filesystem::copy 差不多,但多出两件事:权限继承控制、异步支持。
-
CopyFile默认不保留时间戳,要保留得用CopyFileEx并传COPY_FILE_COPY_SYMLINK标志(其实名字误导,它也管时间戳) -
CreateFile打开源文件时,dwShareMode设为FILE_SHARE_READ,否则其他进程读不了原文件 - 目标路径父目录不存在时,
CopyFile不自动创建 —— 得先调CreateDirectory或SHCreateDirectory - 返回值是
BOOL,失败时用GetLastError()查具体原因,比如ERROR_ACCESS_DENIED
BOOL ok = CopyFile(L"src.txt", L"dst.txt", FALSE); // FALSE = 不交互覆盖
if (!ok) {
DWORD err = GetLastError(); // 别忽略这个
}
实际项目里,80% 的情况用 std::filesystem::copy 加 error_code 版就足够;真碰上权限、符号链接、或嵌入式受限环境,才需要切到流式读写或 Win32。最常被忽略的是:没人检查 gcount(),也没人捕获 filesystem_error,结果线上复制一半静默失败。







