文件操作之標準I/O函數庫

三、標準I/O函數庫

C標準庫提供了文件的標準 I/O函數庫,相比前述的系統調用,主要差別是實現了跨平臺的用戶態緩衝的解決方案。標準I/O庫使用簡單,與系統調用I/O相似,也包括打開、讀寫、關閉這些操作。

系統調用是操作系統直接提供的函數接口。因爲運行系統調用時,Linux必須從用戶態切換到內核態,執行相應的請求,然後再返回到用戶態,所以應該儘量減少系統調用的次數,從而提高程序的效率。

1、文件指針和流

系統級的I/O操作函數都是針對文件描述符的。即打開文件時返回一個文件描述符,然後可以直接對該文件描述符進行操作。

對於標準I/O操作函數來說,打開或創建一個文件時,會返回一個指向FILE對象的指針。該FILE對象通常是一個結構體,它包含了I/O函數庫爲管理該FILE對象所需要的儘可能多的信息。包括用於實際I/O文件的文件描述符,指向流緩存的指針,緩存長度等。

文件指針與文件描述符是一一對應的關係,這種對應關係由標準I/O庫自己內部維護。應用程序調用時,只需要提供文件指針即可。文件指針指向的數據類型爲FILE型,但應用程序無須關係它的具體內容。

在標準I/O中,一個打開的文件稱爲流(stream),流可以用於讀(輸入流)、寫(輸出流)或者是讀寫(輸入輸出流)。每個進程在啓動後就會打開三個流,與打開的三個文件相對應:stdin代表標準輸入流,stdout代表標準輸出流,stderr代表標準錯誤輸出流(與地層文件描述符0,1和2相對應),它們都是(FILE*)型的指針。標準錯誤輸出流不進行緩衝,輸出的內容會馬上同步到文件(控制檯設備)。

2、標準 I/O編程

2.1、打開和關閉流

fopen函數用於打開一個文件流,其原型如下:

FILE *fopen(const char *filename, const char *mode);

 filename:被打開的文件的名稱(可包含路徑)。
 mode:字符串,用於表示打開的模式。
 返回值:打開成功後的文件指針,失敗則返回NULL。

 mode如下:(此參數是一個字符串,而不是一個字符,所以應該使用雙引號)

 字符串    含義
 “r” 或 “rb”   以只讀方式打開
 “w” 或 “wb”  以只寫方式打開,若文件有內容,則清空
 “a” 或 “ab”   以只寫方式打開,原內容保留,寫入的內容附加在文件流尾部
 “r+” 或 “rb+ "或 "r+b"  以更新方式打開,此時文件可讀可寫
 “w+” 或 wb+ 或"w+b " 以更新方式打開,文件可讀可寫,但打開時清空文件內容
 “a+” 或 "ab+ "或 "a+b " 以更新方式打開,文件可讀可寫,寫入的內容附加在文件流尾部

字母b表示文件是一個二進制文件,而不是一個文本文件。

fclose 函數用於關閉文件,其原型如下:

 int fclose(FILE *stream);

 這個函數可以改變 stream參數所代表的文件,如果改變成功則返回 0,否則將返回 EOF 並且設置變量errno 的值以指示錯誤。
改變前會自動將文件流中的數據寫入文件。

2.2、讀和寫流

一旦打開了流,則可以在三種不同類型的非格式化I/O中進行選擇,對其進行讀寫操作:
(1).每次一個字符的I/O
(2).每次一行的I/O
(3).數據塊I/O,數據塊I/O操作讀或寫一定數量的對象,而每個對象具有指定的長度。fread, fwrite函數常用於每次讀或寫一個結構。

2.3、每次一個字符的I/O

字符輸出函數:

int fgetc(FILE *stream);

int getc(FILE *stream);

int getchar(void);

fgetc 函數用於從文件流中讀取一個字符,與它功能相同的函數時 getc,其中 stream 參數是要讀取的文件流。它們返回值雖然是整型,但實際表示的是讀到的字符,只不過進行了類型轉換。如果讀操作發送錯誤或者到達文件尾,則返回值是 EOF。
getc 與 fgetc 的區別在於它可能是由宏定義實現的,因此參數可能在宏展開以後被使用多長,如果參數本身是一個表達式就會被多長求值,這種情況在使用中應該避免。
getchar 函數用於從標準輸入流讀取一個字符,實際上對 getchar的調用完成等價於getc(stdin),它從標準輸入裏讀取下一個字符。

字符輸入函數:

int putc(int c, FILE * stream) ;

int fputc(int c, FILE * stream) ;

int putchar(int c) ;

fputc 函數用於向文件流寫入一個字符,其原型如下:putc函數與它的功能相同:

c:是要寫入的字符,它雖然是整型,但寫入時會將其轉換爲無符號字符型。
stream:要寫入的文件指針。
返回值:寫入的字符轉換成整型後的值,發送錯誤則返回EOF。

 putc 函數與fputc函數的區別在於它有可能是用宏定義實現的。

 putchar 用於向標準輸出寫入一個字符,與putc等同:

 putc(c,stdout),把單個字符寫到標準輸出。

2.4、每次一行的I/O

行輸出函數:

char * gets(char *s)

char fgets(char * s, int size, FILE * stream)

 fgets 用於從文件流中讀取一行數據:

 s:指向一個緩衝區,用於存放讀到的數據。
 size:讀取的字節數上限,實際讀取的字節數不會超過 size-1。
 stream:要讀取的文件指針。
 返回值:等於 s,如果有錯誤發生或文件結束,則返回 NULL。

 用 fgets函數讀取數據時,當讀到一個換行符,或者文件結束,或者讀取的字節數達到 size-1,則讀取操作不再繼續,函數返回。fgets 函數還會在讀到的數據最後加一個字符 \0,使之變成一個合法的字符串。注意,如果讀到換行符,則換行符也在讀到數據中。

 gets 函數用於從標準輸入讀取一行數據,參數 s指向用於存放數據的緩衝區,如果讀取成功則返回值就是s,否則返回 NULL。

 gets 是一個不提倡使用的函數,因爲它對讀入的字節數沒有控制,緩衝區是否會溢出完全取決於用戶的輸入。

行輸入函數:

int fputs(const char *str, FILE *fp)

int puts(const char *str)

 fputs 函數用於向文件流寫入一個字符串,其原型如下:

 s:要寫入的字符串,必須是以 \0結尾的合法字符串。
 stream:要寫入的文件指針。
 返回值:非負數表示寫入成功,有錯誤發生則返回 EOF。

 fputs 函數在向文件流寫入字符串時,字符串的結束符 \0並不會被寫入。
 puts 函數將字符串寫入標準輸出, 其中 s參數是要寫入的字符串,它的返回值的含義與 fputs 函數相同。
 與 fputs 函數不同的是,puts 函數在將字符串寫入之後會再寫入一個換行符。

2.5、數據塊I/O

size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)

 fread函數用於從打開的文件流中讀數據:

 ptr:指向用於存放讀取到的數據的緩衝區。
 size:被讀取的數據塊的長度。
 nitems:要讀取的數據塊的個數。
 stream:被讀取的文件指針。
 返回值:實際讀取到的數據塊的個數。

 使用 fread函數需要注意的是,它以數據塊(或稱記錄)爲單位進行讀取,返回值也是成功讀取的數據塊的個數,而不是字節數,這個數目有可能比要讀取的個數 nitems 少。

size_t fwrite(const void * ptr,size_t size, size_t nmemb, FILE * stream)

 fwrite 函數用於向打開的文件流寫入數據:

 ptr:指向存放寫入數據的緩衝區。
 size:要寫入的數據塊的長度。
 nitems:要寫入的數據塊的個數。
 stream:要寫入的文件指針。
 返回值:實際寫入的數據塊的個數。

 與 fread函數類似,fwrite 函數也是數據塊爲單位向文件流寫入數據的。

2.6、格式化輸出

fprintf 是向文件流格式化寫入數據的函數,其原型如下:

int fprintf(FILE *stream, const char *format,...);

stream:要寫入的文件指針。
format:格式字符串。
可變參數:要寫入的數據。
返回值:如果寫入成功,則返回格式化後字符串的長度,也就是寫入數據的長度,負數表示有錯誤發生。

常用輸出轉換符:

 格式符      功能

 %d 或 %i    按有符號十進制格式輸出整型參數

 %u         按無符號十進制格式輸出無符號整型參數

 %o      按無符號八進制格式輸出無符號整型參數

 %x      按無符號十六進制格式輸出無符號整型參數,使用字母 a,b,c,d,e,f

 %X      按無符號十六進制方式輸出無符號整型參數,使用字母 A,B,C,D,E,F

 %c      將整型參數轉換爲無符號字符型,並輸出爲字符

 %f       按十進制格式輸出高精度浮點型參數

 %e      按科學計數法格式輸出高精度浮點型參數,使用字母 e

 %E      按科學計數法格式輸出高精度浮點型參數,使用字母 E

 %g 或 %G     可理解爲系統自帶選擇 %f 或 %e 格式輸出

 %p      按十六進制格式輸出指針型參數

 %s                  將字符指針型參數視爲字符串輸出

 因爲格式字符串中的符號 %有了特殊的含義,所以要原樣輸出一個 %,則需要連續寫兩個 %,即 %%。

 常用輸出格式符標誌(放在 %的後面):

 字符              作用

 數字 0    當輸出數字時,填充 0 而不是空格

 減號 - 修改爲左對齊方式,空格填充在右邊

 空格  對應正數來說,左邊預留一個空格作爲符號位

 加號 + 總是在正數左邊加上 + 符號,在負數左邊加上 - 符號

我們常用的 printf 函數實際上是對 fprintf 函數的包裝,它用來向標準輸出寫入格式的字符串,其原型如下:

int printf(const char *format, ...);

 它比 fprintf函數少一個文件指針參數,因爲這個文件指針一定是 stdout。

 與格式化輸出相關的還有一個函數 sprintf,它並不是文件 I/O 操作,而是將格式化的字符串輸出到一個緩衝區中,原型:

 int sprintf(char *str, const char *format,...);

 其中 str參數就指向用於存放結果的緩衝區。sprintf 函數會在輸出字符串的末尾加上結束符 \0。使用這個函數時要注意,str

指向的緩衝區要有足夠的大小來容納生成的字符串,否則就有內存訪問越界的問題。很多情況下並不能事先知道結果字符串的長度,這時可

以用下面這個函數:

 int snprintf(char *str, size_t size, constchar *format, ...);

size:限制生成字符串的長度,即寫入緩衝區的字節數。如果格式化後的字符串長度等於或大於 size,則只寫入前 size-1個字節,然後寫入結束符 \0。

返回值:格式化後的字符串長度。

2.7、格式化輸入函數

fscanf 可以從文件流以一定的格式讀取數據,其原型如下:

 int fscanf(FILE *stream, const char *format, ...);

  stream:要讀取的文件指針。

  format:格式字符串。

 可變參數:一般是指針,指向用於存儲到的數據流量。

 返回值:成功解析的數據項的個數(不是字節數),失敗則返回 EOF

 格式字符串中的字符將與輸入流中讀到的字符進行匹配,具體來說有以下幾種情況。

 空白字符:包括空格、製表、換行等字符,將與輸入流中的連續 0個或多個空白字符相匹配,也就是說,一個空白字符可以消 耗多個空白字符。

 普通字符:不想與從輸入流讀入的字符相同。

 轉換符:以符合 %開始的多個字符,這時輸入流中讀入的字符將按某種格式解析爲數據,存入對應的可變參數指向的變量中。

 常用輸入轉換符:

 轉換符           作用

 %d        以十進制格式讀入整數,存在整型變量中

 %i         當下一個字符是 0時,以八進制格式讀取整數;當下兩個字符是 0x 或 0X 時,以十六進制格式讀入整數;否則以十進制格式讀入整數,存放在整型變量中。

 %u        以十進制格式讀入整數,存放在無符號整型變量中

 %o           以八進制格式讀入整數,存放在無符號整型變量中

 %x 或 %X       以十六進制格式讀入整數,存放在無符號整型變量中

 %f,%g,%e 或 %E   讀入浮點數,存放在浮點型變量中

 %s      讀入字符串,字符串從下一個非空白字符開始,再遇到一個空白字符或者達到指定的域寬後結束。字符串存放在對應的參數指向的緩衝區中,末尾會自動加上 \0

 %c     讀入域寬所指定個數的字符,默認是一個。不跳過開始的空白字符,讀入的字符放在對應參數指向的字符數組中,末尾不加 \0

  scanf 函數類似於 fscnaf函數,只不過是從標準輸入讀取數據,原型:

int scanf(const char *format, ...);

還有一個 sscanf 函數可以從字符串中格式化讀取數據,原型:

 int sscanf(const char *str, const char*format, ...);

 其中,str參數就是被讀取的字符串

2.7、定位流

 fseek函數的功能是把當前位置設置到offset處,whence參數決定了相對於文件的位置,其原型如下:
 int fseek(FILE *stream, long offset, int whence);
  stream:被操作的文件指針。
  offset:讀寫位置的偏移量。
  whence:用於指定偏移量的相對啓點。
  返回值:0 表示操作成功, -1 表示操作失敗並且設置 errno 變量的值爲錯誤碼。
  whence 參數的取值及含義:
  SEEK_SET:表示偏移量相對於文件的開頭。
  SEEK_CUR:表示偏移量相對於當前的讀寫位置。
  SEEK_END:表示偏移量相對於文件末尾。
 
 如果要將讀寫位置移動到文件的開頭,還可以使用這個函數:
 void rewind(FILE *stream);

 ftell 函數可以得到文件流的讀寫位置,其原型:
 long ftell(FILE *stream);
 參數 stream 是文件指針,返回值就是文件流的當前讀寫位置(相對於文件開頭)。

2.8、標準 I/O 錯誤處理:
 當標準 I/O 操作發送錯誤時,比如返回 NULL 指針或者 EOF,可以通過讀 errno 變量得到錯誤碼。
 更方便的是使用標準 I/O 的錯誤判斷函數,如:
 int ferror(FILE *stream);
 int feof(FILE *stream);
ferror函數檢查文件在用各種輸入輸出函數進行讀寫時是否出錯。返回0表示沒有錯,否則有錯。
feof函數判斷文件是否處於文件結束位置,如文件結束返回1,否則返回0。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章