69-c语言大文件拷贝
高效C语言大文件拷贝应采用块读写策略(如1MB缓冲区),通过
fread和fwrite批量传输数据,避免按字符读取的低效。需使用二进制模式("rb"和"wb")打开文件,并基于fread返回的实际字节数正确处理文件末尾。此方法比C++流操作快20倍以上(可达350 MB/s),显著减少系统调用次数,充分利用文件系统缓存,是处理大文件拷贝的最优方案。
在C语言中进行大文件拷贝时,选择合适的拷贝方法对性能影响巨大。
为什么需要高效拷贝?
从基准测试可以看出,C语言实现的文件拷贝速度比C++快20倍以上:
- C语言:350.345 MB/s
- C++:21.7815 MB/s
常见拷贝方法比较
1. 按字符读取复制(效率最低)
FILE *fp1 = fopen(argv[1], "rb");
FILE *fp2 = fopen(argv[2], "wb");
char ch;
while (feof(fp1) == 0) {
ch = fgetc(fp1);
fputc(ch, fp2);
}
问题:每次只读取一个字符,频繁调用系统函数,效率极低。
2. 按块读取复制(改进版)
#define MAXSIZE 1024*1024*10 // 10MB缓冲区
int main(int argc, char *argv[]) {
unsigned int start_time = time(NULL);
if (argc < 3) {
printf("缺少参数\n");
return -1;
}
FILE *fp1 = fopen(argv[1], "rb");
FILE *fp2 = fopen(argv[2], "wb");
if (!fp1 || !fp2) {
printf("操作文件失败\n");
return -2;
}
char ch[MAXSIZE];
while (!feof(fp1)) {
memset(ch, 0, MAXSIZE);
int len = fread(ch, 1, MAXSIZE, fp1);
fwrite(ch, len, 1, fp2);
}
fclose(fp1);
fclose(fp2);
unsigned int end_time = time(NULL);
printf("花费时间:%d(s)\n", end_time - start_time);
return 0;
}
问题:fread函数的使用方式不正确,容易导致文件大小不一致。
3. 按块读取复制(正确实现)
#define BUFFER_SIZE 1024*1024 // 1MB缓冲区
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("缺少参数\n");
return -1;
}
FILE *fp1 = fopen(argv[1], "rb");
FILE *fp2 = fopen(argv[2], "wb");
if (!fp1 || !fp2) {
printf("操作文件失败\n");
return -2;
}
char buffer[BUFFER_SIZE];
size_t bytesRead;
while ((bytesRead = fread(buffer, 1, BUFFER_SIZE, fp1)) > 0) {
fwrite(buffer, 1, bytesRead, fp2);
}
fclose(fp1);
fclose(fp2);
return 0;
}
关键点:
- 使用
fread和fwrite进行块读写,避免频繁系统调用 - 通过
bytesRead变量获取实际读取的字节数,正确处理文件末尾 - 采用1MB缓冲区(可调整),平衡内存使用和性能
为什么这个方法高效?
- 减少系统调用次数:每次读写1MB数据,而不是1个字符
- 利用文件系统缓存:操作系统会自动处理文件系统缓存,提高I/O效率
- 避免feof()的陷阱:
feof()在文件末尾时需要额外读取才能设置EOF标志
高性能实现(快速版)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#define BUFFER_SIZE 1024*1024 // 1MB缓冲区
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("缺少参数\n");
return -1;
}
unsigned int start_time = time(NULL);
FILE *fp1 = fopen(argv[1], "rb");
FILE *fp2 = fopen(argv[2], "wb");
if (!fp1 || !fp2) {
printf("操作文件失败\n");
return -2;
}
// 获取文件大小
struct stat st;
if (stat(argv[1], &st) == 0) {
printf("文件大小: %ld bytes\n", st.st_size);
}
char buffer[BUFFER_SIZE];
size_t bytesRead;
while ((bytesRead = fread(buffer, 1, BUFFER_SIZE, fp1)) > 0) {
fwrite(buffer, 1, bytesRead, fp2);
}
fclose(fp1);
fclose(fp2);
unsigned int end_time = time(NULL);
printf("拷贝完成,耗时: %d秒\n", end_time - start_time);
return 0;
}
重要注意事项
- 文件打开模式:务必使用"rb"和"wb"模式,确保二进制文件正确读写
- 缓冲区大小:1MB是常见选择,但可根据实际需求调整(如10MB)
- 错误处理:检查
fopen、fread和fwrite的返回值 - 文件末尾处理:使用
fread返回的实际读取字节数,而非固定大小 - 跨平台:在Windows上需要使用"rb"和"wb"模式,Linux默认使用"r"和"w"即可
为什么C比C++快?
从基准测试可以看出,C语言直接使用fread和fwrite比C++的std::copy或out << in.rdbuf()快20倍以上。这是因为:
- C语言的文件操作函数更底层,直接与操作系统交互
- C++的流操作增加了额外的抽象层和检查
- C语言可以更精确地控制缓冲区大小和I/O操作
总结
对于大文件拷贝,使用块读写方式(如1MB缓冲区)比按字符读写效率高得多。正确实现的关键在于:
- 选择合适的缓冲区大小
- 使用
fread和fwrite进行块操作 - 正确处理文件末尾情况
- 使用二进制模式("rb"和"wb")
这种实现方式可以达到350 MB/s以上的拷贝速度,远超其他实现方式,是处理大文件拷贝的高效方法。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 软件从业者Hort
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果

