Skip to content

Latest commit

 

History

History
84 lines (55 loc) · 3.67 KB

File metadata and controls

84 lines (55 loc) · 3.67 KB

00 - 前言与需求

直接用 cp 多没意思

说真的,当我们需要拷贝一个文件的时候,第一反应肯定是 cp source dest。这命令简单、可靠、系统自带,有什么理由重新发明轮子呢?

但事情没那么简单。假设你正在拷贝一个 10GB 的大文件,cp 命令执行后,你就只能干等着。拷贝到多少了?速度多少?还要等多久?这些信息 cp 统统不会告诉你。在拷贝完成之前,你就像个黑盒一样,不知道进度如何。

当然你可以用 rsync --progress,这确实能看到进度。但如果我们想自己实现一个类似的功能呢?更重要的是,在这个过程中我们能学到什么?

作为一个学习项目

文件拷贝表面上看是个简单的任务,但实际上它涉及了很多实用的编程技巧:

  1. 文件 I/O:如何高效地读写文件?一次读多少合适?
  2. 错误处理:文件不存在怎么办?权限不足怎么办?磁盘满了怎么办?
  3. 进度反馈:如何实时显示进度?如何计算速度和 ETA?
  4. 终端控制:如何实现单行更新的进度条?
  5. 时间处理:如何精确测量时间差?

这些都是通用技能,学会了不只可以用在文件拷贝上,任何需要长时间运行的任务都能用到。

我们的目标

在这个教程里,我们要实现一个 FileCopier 类,它具备以下功能:

  • 支持任意大小的文件拷贝
  • 实时显示拷贝进度(百分比、已拷贝/总大小)
  • 显示当前拷贝速度
  • 预估剩余时间(ETA)
  • 支持自定义块大小
  • 完善的错误处理

接口设计上,我们希望它足够简单:

FileCopier copier;
bool success = copier.copy("source.txt", "dest.txt");

或者指定块大小:

FileCopier copier(64 * 1024);  // 64KB 块大小
copier.copy("large_file.bin", "backup.bin");

环境说明

这个项目需要 C++17 支持,因为我们会用到 std::filesystem 库。这是 C++17 引入的文件系统库,让跨平台的文件操作变得非常简单。

编译器要求:

  • GCC 8+ 或 Clang 7+(Linux/macOS)
  • MSVC 2017+(Windows)

构建工具:

  • CMake 3.10+

为什么需要分块读写

在开始编码之前,我们先思考一个问题:为什么不直接一次性读整个文件到内存,然后写出去?

// 危险!不要这样写!
std::vector<char> buffer(file_size);
in.read(buffer.data(), file_size);
out.write(buffer.data(), file_size);

这个做法对小文件没问题,但对大文件就是灾难性的。10GB 的文件就要分配 10GB 的连续内存,这在大多数系统上都不现实。而且一次性读入也没有办法显示进度,因为要么读完了要么没读完,没有中间状态。

正确的做法是分块读写:每次读取一块(比如 8KB),处理完再读下一块。这样:

  1. 内存占用恒定,不管文件多大
  2. 可以在每块之间更新进度显示
  3. 更好的缓存利用率(数据在 CPU 缓存里停留时间更短)

块大小的选择也有讲究:太小会导致系统调用次数增多,太大可能导致内存压力和缓存失效。8KB 是一个不错的起点,它在大多数系统上都是内存页的整数倍,能较好地平衡性能。

下一步

好了,设计思路有了,下一章我们开始写代码。先实现 FileCopier 类的基本框架和分块读写逻辑,进度条和花里胡哨的功能后面再加。记住,编程的最佳实践是把问题拆解,一步步来,不要试图一口气写完所有功能。

下一章:类设计与分块读写 →