1. 程式人生 > 其它 >20191302董佳帥第九章學習筆記

20191302董佳帥第九章學習筆記

第九章 I/O庫函式

I/O庫函式與系統呼叫

  • 系統呼叫函式
    • open( ):開啟和建立檔案
      • int open(const char * pathname, int flags, mode_t mode);
    • read( ):讀檔案
      • ssize_t read[1] (int fd, void *buf, size_t count);
    • write( ):寫檔案
      • ssize_t write (int fd, const void * buf, size_t count);
    • lseek( )
      • 每一個已開啟的檔案都有一個讀寫位置, 當開啟檔案時通常其讀寫位置是指向檔案開頭, 若是以附加的方式開啟檔案(如O_APPEND), 則讀寫位置會指向檔案尾. 當read()或write()時, 讀寫位置會隨之增加,lseek()便是用來控制該檔案的讀寫位置. 引數fildes 為已開啟的檔案描述詞, 引數offset 為根據引數whence來移動讀寫位置的位移數.
      • off_t lseek(int fildes, off_t offset, int whence);
    • close( ):關閉檔案
      • int close(int fd);
  • I/O庫函式
    • fopen( ):以指定的形式開啟檔案

      r 以只讀方式開啟檔案,該檔案必須存在。
      r+以讀/寫方式開啟檔案,該檔案必須存在。
      rb+以讀/寫方式開啟一個二進位制檔案,只允許讀/寫資料。
      rt+以讀/寫方式開啟一個文字檔案,允許讀和寫。
      w開啟只寫檔案,若檔案存在則長度清為0,即該檔案內容消失,若不存在則建立該檔案。
      w+開啟可讀/寫檔案,若檔案存在則檔案長度清為零,即該檔案內容會消失。若檔案不存在則建立該檔案。
      a以附加的方式開啟只寫檔案。若檔案不存在,則會建立該檔案,如果檔案存在,寫入的資料會被加到檔案尾,即檔案原先的內容會被保留(EOF符保留)。
      a+以附加方式開啟可讀/寫的檔案。若檔案不存在,則會建立該檔案,如果檔案存在,則寫入的資料會被加到檔案尾後,即檔案原先的內容會被保留(原來的EOF符 不保留)。
      wb以只寫方式開啟或新建一個二進位制檔案,只允許寫資料。
      wb+以讀/寫方式開啟或建立一個二進位制檔案,允許讀和寫。
      wt+以讀/寫方式開啟或建立一個文字檔案,允許讀寫。
      at+以讀/寫方式開啟一個文字檔案,允許讀或在文字末追加資料。
      ab+以讀/寫方式開啟一個二進位制檔案,允許讀或在檔案末追加資料。

    • fread( ):從給定流 stream 讀取資料到 ptr 所指向的陣列中。
      • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

      ptr -- 這是指向帶有最小尺寸 size*nmemb 位元組的記憶體塊的指標。
      size -- 這是要讀取的每個元素的大小,以位元組為單位。
      nmemb -- 這是元素的個數,每個元素的大小為 size 位元組。
      stream -- 這是指向 FILE 物件的指標,該 FILE 物件指定了一個輸入流。

    • fwrite( ):把 ptr 所指向的陣列中的資料寫入到給定流 stream 中.
      • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
    • fseek( ):設定流 stream 的檔案位置為給定的偏移 offset,引數 offset 意味著從給定的 whence 位置查詢的位元組數。
      • int fseek(FILE *stream, long int offset, int whence)

      stream -- 這是指向 FILE 物件的指標,該 FILE 物件標識了流。
      offset -- 這是相對 whence 的偏移量,以位元組為單位。
      whence -- 這是表示開始新增偏移 offset 的位置。它一般指定為下列常量之一。

    • fclose( ):關閉流 stream。重新整理所有的緩衝區。
      • int fclose(FILE *stream)

I/O庫函式的演算法

  • fread演算法
    1. 第一次呼叫時,fread()使用儲存的檔案掃描符fd發出 n=read(fd, fbuffer, BLKSIZE);系統呼叫,用資料塊填充內部的fbuff[];
    2. 初始化fbuff[]指標、計數器和狀態變數;
    3. 將資料複製到程式緩衝區;
    4. 若內部緩衝沒有足夠的資料,則使用read()繼續填充內部緩衝區,並將資料從內部緩衝區複製到程式緩衝區;
    5. 複製完之後,更新內部緩衝區的指標、計數器,為下次read()做準備。
  • fwrite演算法
    1. 將資料寫入內部緩衝區,調整緩衝區指標、計數器和狀態變數;
    2. 若緩衝區滿,則呼叫write()將緩衝區寫入系統核心。
  • fclose演算法
    1. 關閉檔案流區域性緩衝區;
    2. 發出close(fd)系統呼叫關閉file結構體檔案描述符;
    3. 釋放file結構體,並將file指標重置為null。

I/O庫模式

  • 字元模式I/O
    • int fgetc(FILE *fp);
      • 該函式以無符號 char 強制轉換為 int 的形式返回讀取的字元,如果到達檔案末尾或發生讀錯誤,則返回 EOF。
    • int ungetc(int c, FILE *fp);
      • 如果成功,則返回被推入的字元,否則返回 EOF,且流 stream 保持不變。
    • int fputc(int c, FILE *fp);
      • 如果沒有發生錯誤,則返回被寫入的字元。如果發生錯誤,則返回 EOF,並設定錯誤識別符號。
  • 行模式I/O
    • char *fgets(char *buf, int size, FILE *fp);
      • 從fp中讀取最多的為一行(以\n結尾)的字元。
    • int fputs(char *buf,FILE *fp);
      • 將buf中的一行寫入fp中。
  • 格式化I/O
    • 格式化輸入
      • scanf(char *FMT,&items);
      • fscanf(fp, char *FMT,&items);
    • 格式化輸出
      • printf(char *FMT,items);
      • fprintf(fp,char *FMT,items)
  • 記憶體中的轉換函式
    • sscanf(buf,FMT,&items);
    • sprintf(buf, FMT, items);
  • 其他I/O庫函式
    • fseek()、ftell()、rewind()
      • 更改檔案流中的讀/寫位元組位置
    • feof()、ferr()、fileno()
      • 測試檔案流狀態
    • fdopen()
      • 用檔案描述符開啟檔案流
    • freopen()
      • 以新名稱重新開啟現有的流
    • setbuf()、setvbuf()
      • 設定緩衝方案
    • popen()
      • 建立管道,復刻自程序來呼叫sh

檔案流緩衝

  • 每個檔案流都有一個FILE結構體,其中包含一個內部緩衝區。對檔案流進行讀寫需要遍歷FILE結構體的內部緩衝區。檔案流可以使用三種緩衝方案中的一種。
  • 無緩衝:從非緩衝流中寫入或讀取的字元將盡快單獨傳輸到檔案或從檔案中傳輸。
    行緩衝:遇到換行符時,寫入行緩衝流的字元以塊的形式傳輸。
    全緩衝:寫入全緩衝流或從中讀取的字元以塊大小傳輸到檔案或從檔案傳輸。這是檔案流的正常緩衝方案。

  • setvbuf()
    • 定義流 stream 應如何緩衝。
    • int setvbuf(FILE *stream, char *buffer, int mode, size_t size);
  • fflush()
    • 重新整理流 stream 的輸出緩衝區。
    • int fflush(FILE *stream);

最有收穫的內容

之前在學習c語言時,我對gets()函式和puts()函式就沒有掌握好,他們和scanf()與printf()的區別也沒有弄清,通過學習本章和自己進一步上網查詢,明白了他們之間的區別:

1、 gets可以接收空格;而scanf遇到空格、回車和Tab鍵都會認為輸入結束,所有它不能接收空格。
例如:如果輸入為"hello world"時,上面程式的執行結果是"hello world"。而如果用scanf則只能輸出hello
2、scanf對末尾回車符的處理:把回車符保留在快取中。gets對末尾回車符的處理:接收回車,但把回車替換為\0.
3、gets的返回值為char*型,當讀入成功時會返回輸入的字串指標地址,出錯時返回NULL;scanf返回值為int型,返回實際成功賦值的變數個數,當遇到檔案結尾標識時返回EOF。
4、gets函式僅用於讀入字串;scanf為格式化輸出函式,可以讀入任意C語言基礎型別的變數值,而不是僅限於字串型別。

問題

實踐練習

編寫一個c程式,將文字檔案中的字母由小寫轉換為大寫

程式截圖:

#include <stdio.h>
FILE *fp,*gp;
int main(){
        char c;
        fp=fopen("source.txt","r"); 
        gp=fopen("target.txt","w");
        while((c=fgetc(fp))!=EOF){
                if(c>='a'&&c<='z')c=c-32;
                fputc(c,gp);
        } 
        fclose(fp); 
        fclose(gp); 

        return 0;
}   

原始檔

執行後文件