利用标准I/O函数,实现两个文件的复制功能

主要使用函数原型:

1.每次读写一个字符:

int fgetc(FILE *stream);
int fputc(int c, FILE *stream);

2.每次读写一行字符:

char *fgets(char *s , int size , FILE *stream);
char *fputs(const char *s ,FILE *stream);

3.每次读写一个块字符:

size_t fread(void *ptr,size_t size,xize_t nmemb,FILE\*stream);
size_t fwrite(const void *ptr,size_t size,xize_t nmemb,FILE\*stream);

4.另外,每个函数也使用了一些辅助函数,主要列举如下:

int feof(FILE *stream);
int ferror(FILE *stream);
long ftell(FILE *stream); //获取当前的位置偏移量

实现过程中几个易错细节小结

  • 几个读取函数的文件指针偏移

    int fgetc(FILE *stream),char *fgets(char *s , int size , FILE *stream),与size_t fread(void *ptr,size_t size,xize_t nmemb,FILE*stream)的每次读取,都是从文件的开始位置,向后读取;在下一次循环到来之前,文件内的当前位置,已经是已读取的字符之后。

    笔者一开始图方便,将读取函数fgetc()的返回值直接作为写入函数fputc()的参数,写了下面这行代码,就导致了一个比较低级的错误:

        while (!feof(fp_src)) // 每次读写一个字符的第19~33行简易实现
        {
            fputc(fgetc(fp_src), fp_dst);
        }
    

    导致复制出来的文件末尾都有一个乱码:

        fclose(fp_dst); // 关闭并释放文件指针堆空间
        fclose(fp_src);
        return 0;
    }�
    

    因此,此处的循环结束条件必须加上fgetc(fp_src)==EOF。

  • 行与块的区别

    行与块的区别,影响到设计的函数结构。

    行:即数据至多包含一个换行符“\n”,遇到换行符就进行下一次循环;因此,可以以fgets()是否为NULL为循环结束条件,即文件stream到达文件末尾即可。

    块:即一个固定的缓存空间,当数据块中出现换行符或字符串结束标记符等都不会受影响。因此将一个文件分为若干块,将不满一块时的判断条件作为循环结束条件,即:
    *size_t fread(void ptr,size_t size,xize_t nmemb,FILE*stream)< NMEMB

    并将剩下的字符继续输入。

  • 函数返回值

    如同第二点,返回值非常重要,可以以此为切入口设计程序架构。

    单字符 单行
    读取函数 int fgetc(FILE *stream); char *fgets(char *s , int size , FILE *stream); size_t fread(void *ptr,size_t size,xize_t nmemb,FILE*stream);
    成功/失败 读取到的字符/EOF(-1) 指针s/NULL nmemb/<nmemb
    写入函数 int fputc(int c, FILE *stream); char *fputs(const char *s ,FILE *stream); size_t fwrite(const void *ptr,size_t size,xize_t nmemb,FILE*stream);
    成功/失败 读取到的字符/EOF(-1) true/NULL nmemb/<nmemb

函数:每次读写一个字符


#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
    if (3 != argc) // 判断用户输入的参数是否有效。
    {
        perror("argument is invalid.\n");
        exit(1);
    }
    // 分别打开待拷贝和目标拷贝文件
    FILE *fp_src = fopen(argv[1], "rb"); // 以二进制形式读取第一个文件
    FILE *fp_dst = fopen(argv[2], "wb"); // 以二进制形式追加写入第二个文件
    if (!fp_dst || !fp_src)              // 如果打开错误,则直接打印错误信息并退出.
    {
        perror("fopen()");
        exit(1);
    }
    int data = fgetc(fp_src); // 设置变量,存放fgetc得到的字符
    while (1)
    {
        if (feof(fp_src) && data == EOF) // 如果当前光标已经到文件尾,则退出循环
        {
            printf("Copy completed.\n");
            break;
        }
        else if (ferror(fp_src)) // 如果出现未知错误,则退出
        {
            perror("fgetc()");
            exit(1);
        }
        fputc(data, fp_dst);
        data = fgetc(fp_src); // 继续存放fgetc得到的字符
    }
    fclose(fp_dst); // 关闭并释放打开文件申请的堆空间
    fclose(fp_src);
    return 0;
}

函数:每次读写一行字符


#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define BUFSIZE 100 // 设置每一个缓冲区,即每一行能复制的最大字节数
int main(int argc, const char *argv[])
{
    if (3 != argc) // 判断用户输入的参数是否有效。
    {
        perror("argument is invalid.\n");
        exit(1);
    }
    // 分别打开待拷贝和目标拷贝文件
    FILE *fp_src = fopen(argv[1], "rb"); // 以二进制形式读取第一个文件
    FILE *fp_dst = fopen(argv[2], "wb"); // 以二进制形式追加写入第二个文件
    if (!fp_dst || !fp_src)              // 如果打开错误,则直接打印错误信息并退出.
    {
        perror("fopen()");
        exit(1);
    }
    char buf[BUFSIZE] = {0}; // 定义缓冲区变量,利用数组实现
    while (1)
    {
        bzero(buf, BUFSIZE);              // 每次操作前,进行清空缓冲区操作,以免内容泄露
        if (!fgets(buf, BUFSIZE, fp_src)) // 获取源文件数据,并判断是否为NULL,如果为NULL有两种情况判断
        {
            if (feof(fp_src)) // 如果当前光标已经到文件尾,则表示复制完成并退出循环
            {
                printf("Copy completed.\n");
                break;
            }
            else if (ferror(fp_src)) // 如果出现未知错误,则退出
            {
                perror("fgetc()");
                exit(1);
            }
        }
        fputs(buf, fp_dst); // 复制完成后,进行粘贴操作
    }
    fclose(fp_dst); // 关闭并释放打开文件申请的堆空间
    fclose(fp_src);
    return 0;
}

函数:每次读写一个块字符


#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define SIZE 100 // 设置每一块能容纳的最大字节数
#define NMEMB 5  // 设置块数
int main(int argc, const char *argv[])
{
    if (3 != argc) // 判断用户输入的参数是否有效。
    {
        perror("argument is invalid.\n");
        exit(1);
    }
    // 分别打开待拷贝和目标拷贝文件
    FILE *fp_src = fopen(argv[1], "rb"); // 以二进制形式读取第一个文件
    FILE *fp_dst = fopen(argv[2], "wb"); // 以二进制形式追加写入第二个文件
    if (!fp_dst || !fp_src)              // 如果打开错误,则直接打印错误信息并退出.
    {
        perror("fopen()");
        exit(1);
    }
    char buf[SIZE * NMEMB] = {0}; // 定义缓冲区变量,利用数组实现
    long pre, cur;
    while (1)
    {
        bzero(buf, SIZE * NMEMB);                    // 每次操作前,进行清空缓冲区操作,以免内容泄露
        pre = ftell(fp_src);                         // 记录当前的偏移量;
        if (fread(buf, SIZE, NMEMB, fp_src) < NMEMB) // 获取源文件数据,并判断异常情况,并进行异常情况判断
        {
            if (feof(fp_src)) // 如果当前光标已经到文件尾,则需要把剩余的内容进行复制,这里利用pre,cur实现
            {
                fread(buf, cur - pre, 1, fp_src);
                fwrite(buf, cur - pre, 1, fp_dst);
                printf("Copy completed.\n");
                break;
            }
            else if (ferror(fp_src)) // 如果出现未知错误,则退出
            {
                perror("fgetc()");
                exit(1);
            }
        }
        fwrite(buf, SIZE, NMEMB, fp_dst); // 复制完成后,进行粘贴操作
    }
    fclose(fp_dst); // 关闭并释放打开文件申请的堆空间
    fclose(fp_src);
    return 0;
}
posted @ 2024-05-08 20:54  Rice_rice  阅读(62)  评论(0编辑  收藏  举报