高效C语言大文件拷贝应采用块读写策略(如1MB缓冲区),通过freadfwrite批量传输数据,避免按字符读取的低效。需使用二进制模式("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;
}

关键点

  1. 使用freadfwrite进行块读写,避免频繁系统调用
  2. 通过bytesRead变量获取实际读取的字节数,正确处理文件末尾
  3. 采用1MB缓冲区(可调整),平衡内存使用和性能

为什么这个方法高效?

  1. 减少系统调用次数:每次读写1MB数据,而不是1个字符
  2. 利用文件系统缓存:操作系统会自动处理文件系统缓存,提高I/O效率
  3. 避免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;
}

重要注意事项

  1. 文件打开模式:务必使用"rb"和"wb"模式,确保二进制文件正确读写
  2. 缓冲区大小:1MB是常见选择,但可根据实际需求调整(如10MB)
  3. 错误处理:检查fopenfreadfwrite的返回值
  4. 文件末尾处理:使用fread返回的实际读取字节数,而非固定大小
  5. 跨平台:在Windows上需要使用"rb"和"wb"模式,Linux默认使用"r"和"w"即可

为什么C比C++快?

从基准测试可以看出,C语言直接使用freadfwrite比C++的std::copyout << in.rdbuf()快20倍以上。这是因为:

  • C语言的文件操作函数更底层,直接与操作系统交互
  • C++的流操作增加了额外的抽象层和检查
  • C语言可以更精确地控制缓冲区大小和I/O操作

总结

对于大文件拷贝,使用块读写方式(如1MB缓冲区)比按字符读写效率高得多。正确实现的关键在于:

  1. 选择合适的缓冲区大小
  2. 使用freadfwrite进行块操作
  3. 正确处理文件末尾情况
  4. 使用二进制模式("rb"和"wb")

这种实现方式可以达到350 MB/s以上的拷贝速度,远超其他实现方式,是处理大文件拷贝的高效方法。