当前位置: 首页 > 工具软件 > fwrite > 使用案例 >

C语言:使用fread和fwrite实现文件拷贝

淳于飞文
2023-12-01

C语言:使用fread和fwrite实现文件拷贝

前置基础

函数一: fread

函数声明:

size_t fread(void *buf, sizeof(void), len, FILE *p);
  • buf , 从p文件中读取的数据
  • sizeof(void) ,每次从p中读取数据的大小
  • len ,每次从p中读取数据的个数
  • p , 打开的文件地址

解释:

sizeof( void ) 和 len 是搭配使用的,它们的作用就是令临时空间使用完:

char a = 0;
fread(&a, 1, 1, p);
//一次使用完
char b[10] = { 0 };
fread(b, sizeof(char), sizeof(b), p);
//每次读取一个char 读取10次。
char c[10] = { 0 };
fread(c, 1, 1, p);

返回值:

fread 的返回值是每次从p中读取的数据的个数,也就是len 的值。

函数二:fwrite

函数声明:

size_t fwrite(void *buf, sizeof(void), int nums, FILE *p);
  • buf , 临时空间,每次从buf中读取数据,然后写入p中
  • sizeof(void) ,每次往p中写入数据块的大小
  • nums ,每次往p中写入的数据
  • p , 打开的文件地址

解释:

fwrite的使用于fread是相互映照的。

返回值:

返回每次写入的次数。

基本思路:

  • 打开要读取的文件a
  • 打开要写入的文件b
  • 读取a ,存放到临时的空间中
  • 读取临时空间的数据,写入b
  • 关闭a
  • 关闭b
  • 如果使用的是堆空间,释放临时空间

代码实现:

mycopy.c :

#include <stdio.h>
void mycopy(char *str1, char *str2)
{
    FILE *p = fopen(str1, "rb");
    FILE *p1 = fopen(str2, "wb");
    unsigned int index = 0; //统计复制了多少次
    while(1)
    {
        char buf[1024] = { 0 };
        fread(buf, sizeof(char), 1024, p);
        if(feof(p))
            break;
        fwrite(buf, sizeof(char), 1024, p1);
        index++;
    }
    fclose(p);
    fclose(p1);
    printf("%d\n", index);
}
int main(int arge, char **arges)
{
    if(arge < 3)
        return 0;
    mycopy(arges[1], arges[2]);
    return 0;
}

解析:

上述代码主要通过一个1kB的空间来实现缓存区,通过缓存区把一个文件的数据读出来,然后把数据写入要复制的文件内。

#编译
gcc -o a mycopy.c
#运行
#把a.txt 复制一份为b.txt
a test1.mp4 test2.mp4

运行结果:

通过运行得到以下下文件

[使用fread和fwrite实现文件拷贝]$ ll
total 21573
-rwxr-xr-x 1 Administrator 197121    55425 三月   21 16:00 a.exe*
-rw-r--r-- 1 Administrator 197121      555 三月   21 18:53 mycopy.c
-rw-r--r-- 1 Administrator 197121 11014383 一月   14  2017 test1.mp4
-rw-r--r-- 1 Administrator 197121 11014144 三月   21 18:59 test2.mp4 
#可以看到文件小于原文件的大小。

代码失误:

上述代码虽然可以把文件给复制下来,但还不能完美的复制下来,test2.mp4 尾数是144,test1.mp4 尾数是383,明显比原文件小。

错误原因:

  1. 每次都要往文件中写入1024个,最后一次会读取到文件末尾,跳出循环,不够1024会舍弃数据。

    测试代码:

    创建一个a.txt

    abcde
    

    文件有5个字节的空间

    -rw-r--r-- 1 Administrator 197121        5 三月   21 19:15 a.txt
    

    代码

    void mycopy_test(char *str1, char *str2)
    {
        FILE *p = fopen(str1, "rb");
        FILE *p1 = fopen(str2, "wb");
        unsigned int index = 0;
        while(1)
        {
            char buf[10] = { 0 };
            fread(buf, sizeof(char), 3, p);//每次只读3个字节的空间
            if(feof(p))
                break;//当读取到文件末尾不够3个字节时,也就是fread和3不相等,会跳出循环。
            fwrite(buf, sizeof(char), 3, p1);
            index++;
        }
        fclose(p);
        fclose(p1);
        printf("%d\n", index);
    }
    

    当最后读取空间不够时,会跳出循环。

    改进后代码:

    void mycopy_test(char *str1, char *str2)
    {
        FILE *p = fopen(str1, "rb");
        FILE *p1 = fopen(str2, "wb");
        unsigned int index = 0;
        while(!feof(p))
        {
            char buf[10] = { 0 };
            fread(buf, sizeof(char), 3, p);
            fwrite(buf, sizeof(char), 3, p1);
            index++;
        }
        fclose(p);
        fclose(p1);
        printf("%d\n", index);
    }
    

    改进后的代码,最后读出不够3个字节,“abcde” ,只读出了2个字节,然后写入到文件b.txt中。

    测试结果:

    [使用fread和fwrite实现文件拷贝]$ ll
    total 21582
    -rwxr-xr-x 1 Administrator 197121    55967 三月   21 19:28 a.exe*
    -rw-r--r-- 1 Administrator 197121        5 三月   21 19:15 a.txt
    -rw-r--r-- 1 Administrator 197121        6 三月   21 19:28 b.txt
    -rw-r--r-- 1 Administrator 197121      910 三月   21 19:28 mycopy.c
    

    可以看到b.txt文件比原文件又大了一个字节。

  2. 所以我们找到了第二个错误,写入字节没控制好。

    解决方法:通过设置读文件字节的返回值,来设置写文件的次数。

    代码实现:

    void mycopy_test(char *str1, char *str2)
    {
        FILE *p = fopen(str1, "rb");
        FILE *p1 = fopen(str2, "wb");
        unsigned int index = 0;
        while(!feof(p))
        {
            char buf[10] = { 0 };
            int res = fread(buf, sizeof(char), 3, p);
            fwrite(buf, sizeof(char), res, p1);
            index++;
        }
        fclose(p);
        fclose(p1);
        printf("%d\n", index);
    }
    

    res 是 fread 读出1个字节的次数,然后把它设为写入1个字节的次数,这样就不会复制失败任何一个字节。

代码提升:

当我们发现我们可以通过设置临时空间来实现文件的复制的时候,我们会非常高兴,但当一个文件非常小的时候,我们设置了一个非常大的临时空间,这就浪费了内存;当文件非常大的时候,我们设计了一个非常小的临时空间,我们函数的循环的时间久会变长,也是非常难受,那有没有一种非常好的方法,每次用多少内存就开辟多少的临时空间呢?

当然是有的!

我们可以通过使用stat函数来获取文件的字节大小,根据文件的大小开辟对应的内存空间,并合理的规划文件的拷贝次数,从而达到文件被拷贝的目的。

代码实现:

#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>

#define NUM 1024 * 10 //10KB

void mycopy_dynamic(char *str1, char *str2)
{
    FILE *p = fopen(str1, "rb");
    FILE *p1 = fopen(str2, "wb");
    struct stat st = { 0 };
    stat(str1, &st);
    int size = st.st_size;
    if(size >= NUM)
        size = NUM;
    char *buffer = malloc(size);
    unsigned int index = 0;
    while(!feof(p))
    {
        int res = fread(buffer, sizeof(char), size, p);
        fwrite(buffer, sizeof(char), res, p1);
        index++;
    }
    free(buffer);
    fclose(p);
    fclose(p1);
    printf("%d\n", index);
}
int main(int arge, char **arges)
{
    if(arge < 3)
        return 0;
    mycopy_dynamic(arges[1], arges[2]);
    return 0;
}

通过上述代码,我们实现了每次拷贝不超过10KB的文件一次拷贝完的操作,如果文件大于10KB,就只申请10KB的内存空间,实现了动态开辟内存来存储临时数据。

测试结果

[使用fread和fwrite实现文件拷贝]$ a test1.mp4 test2.mp4
1076
[使用fread和fwrite实现文件拷贝]$ ll
total 21586
-rwxr-xr-x 1 Administrator 197121    57451 三月   22 15:20 a.exe*
-rw-r--r-- 1 Administrator 197121        5 三月   21 19:15 a.txt
-rw-r--r-- 1 Administrator 197121        6 三月   21 19:28 b.txt
-rw-r--r-- 1 Administrator 197121     1593 三月   22 15:22 mycopy.c
-rw-r--r-- 1 Administrator 197121 11014383 一月   14  2017 test1.mp4
-rw-r--r-- 1 Administrator 197121 11014383 三月   22 15:24 test2.mp4

完结!

 类似资料: