IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言:使用fread和fwrite实现文件拷贝 -> 正文阅读

[C++知识库]C语言:使用fread和fwrite实现文件拷贝

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

完结!

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-24 00:18:35  更:2022-03-24 00:20:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:56:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码