利用標準I/O函式,實現兩個檔案的複製功能

Rice_rice發表於2024-05-08

目錄
  • 主要使用函式原型:
  • 實現過程中幾個易錯細節小結
  • 函式:每次讀寫一個字元
  • 函式:每次讀寫一行字元
  • 函式:每次讀寫一個塊字元

主要使用函式原型:

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;
}

相關文章