Unix/Linux程式設計-標準I/O庫
標準I/O庫
2.1 標準輸入、標準輸出和標準錯誤
程序中預定義了這3個流,可以自動地被程序呼叫。這些流引用的檔案與檔案描述符中的STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO所引用的相同。這3個標準I/O流通過預定義檔案指標stdin、stdout和stderr加以引用,定義在標頭檔案<stdio.h>中。2.2 開啟標準I/O流
#include <stdio.h> FILE *fopen(const char *restrict pathname,const char *restrict type); FILE *freopen(const char *restrict pathname,const char *restrict type, FILE *restrict fp); FILE *fdopen(int fd, const char *type); 返回值:成功返回檔案指標,出錯返回NULL |
3個函式區別如下:
(1) fopen開啟路徑名為pathname的一個指定檔案。
(2) freopen在一個指定的流(fp)上開啟一個指定的檔案,如若該流已經開啟,則先關閉該流。若該流已經定向,則使用freopen清除該定向。一般用於將一個指定的檔案開啟為一個預定義的流:stdin,stdout,stderr。
(3)
type引數指定對該I/O流的讀、寫方式:
Type |
說明 |
open(2)標誌 |
r |
只讀開啟 |
O_RDONLY |
w |
把檔案長度截斷0,寫開啟或建立 |
O_WRONLY|O_CREAT|O_TRUNC |
a |
在檔案為寫,或為寫而建立 |
O_WRONLY|O_CREAT|O_APPEND |
r+ |
為讀寫開啟 |
O_RDWR |
w+ |
檔案長度截斷0,或為讀寫而開啟或建立 |
O_RDWR|O_CREAT|O_TRUNC |
a+ |
在檔案尾讀和寫而開啟或建立 |
O_RDWR|O_CREAT|O_APPEND |
對於fdopen而言,type引數的意義稍有區別,因為該描述符已經被開啟,所以並不會截斷該檔案,真正決定是否截斷該檔案的是原來open的標誌。
在指定w或a型別建立一個新檔案時,我們無法說明該檔案的訪問許可權位,而是由系統的umask值來限制這些許可權。我們可以通過調整umask值來限制我們建立一個新檔案的許可權。
2.3 讀和寫流
2.3.1 每次一個字元的I/O
一次讀或寫一個字元,如果流是帶緩衝的,則標準I/O函式處理所有緩衝。 #include <stdio.h> int getc(FILE *fp); int fgetc(FILE *fp); int getchar(void); 返回值:成功返回一個讀取到的字元,若到檔案尾或出錯,返回EOF |
getchar等同於getc(stdin);getc可以被實現為巨集,而fgetc不能實現為巨集。
不管是出錯還是達到檔案尾端,函式否返回EOF,為了區分這兩種不同的情況,需要嗲用ferror或feof函式。
2.3.2 每次一行的I/O
#include <stdio.h> char *fgets(char *restrict buf, int n, FILE *retrict fp); char *gets(char *buf); 返回值:成功返回buf,達到檔案尾端或出錯返回NULL |
gets從標準輸入讀取,fgets從指定流中讀取n位元組。
fgets函式一直讀到下一個換行符為止,但是不超過n-1個字元。讀到的字元被送入緩衝區buf中。該緩衝區以null位元組結尾。如果改行包括最後一個換行符的字元數超過n-1,則fgets只返回一個不完整的行,但緩衝區總是以null位元組結尾。
gets是一個不推薦使用的函式,有可能造成緩衝區溢位。
2.3.3 二進位制I/O
因為fputs在遇到null位元組時就停止,而在結構中可能包含有null位元組,所以不能實現讀結構的要求。同樣輸入資料中包含null位元組,fgets也不能正確工作。因此,提供了下列兩個函式以執行二進位制I/O操作。 #include <stdio.h> size_t fread(void *restrict prt, size_t size, size_t nobj,FILE *restrict fp); size_t fwrite(const void *restrict prt, size_t size, size_t nobj,FILE *restrict fp); 返回值:讀或寫的物件數 |
2.4 流的定位
#include <stdio.h> long ftell(FILE *fp); 返回值:成功返回當前檔案的位置指示,出錯返回-1L int fseek(FILE *fp,long offset, int whence); 返回值:成功返回0,出錯返回-1 void rewind(FILE *fp); |
2.5 關閉流
#include <stdio.h> int fclose(FILE *fp); 返回值:成功返回0,出錯返回EOF |
當一個程序中止時(直接呼叫exit函式或main函式返回),則所有帶未寫緩衝資料的標準I/O流都被沖洗,所有開啟的標準I/O流都被關閉。
2.6 緩衝
標準I/O庫提供 緩衝的目的是儘可能減少使用read和write呼叫的次數。標準I/O提供了3種類型的緩衝。2.6.1 全緩衝
全緩衝,在填滿標準I/O緩衝區後才進行實際I/O操作。對於駐留在磁碟上的檔案通常是有標準I/O庫實施全緩衝的。術語沖洗(flush)說明標準I/O緩衝區的寫操作。緩衝器可由標準I/O例程自動沖洗,或者呼叫fflush沖洗一個流。
2.6.2 行緩衝
當在輸入和輸出中遇到換行符時,標準I/O庫執行I/O操作。雖然標準庫允許我們一次輸出一個字元,但只有在寫了一行之後才進行實際I/O操作。當設計一個終端時,通常使用行緩衝。對於行緩衝有兩個限制,第一,因為標準I/O庫用來收集每一行的緩衝區的長度是固定的,所以只要填滿了緩衝區,那麼即使還沒有寫一個換行符,也進行I/O操作。第二,任何時候只要通過標準版I/O庫從一個不帶緩衝的流或者一個行緩衝的流得到輸入資料,那麼就會沖洗所有行緩衝輸出流。2.6.3 不緩衝
標準I/O庫不對字元進行緩衝儲存,字元會被立即輸出。標準出錯流strerr通常是不帶緩衝的,這使得出錯資訊可以儘快顯示出來,而不管他們是否含有一個換行符。對任何一個給定的流,如果我們並不喜歡這些系統預設,可以呼叫下列兩個函式中的一個更改緩衝型別:
#include <stdio.h> void setbuf(FILE * fp, char *buf); int setvbuf(FILE *fp, char *buf, int mode, size_t size); 返回值:成功返回0,出錯返回非0 |
使用setvbuf函式,我們可以精確地說明所需的緩衝型別。這是用mode引數實現的:
mode |
說明 |
_IOFBF |
全緩衝 |
_IOLBF |
行緩衝 |
_IONBF |
不到緩衝 |
任何時候,我們都可以強制沖洗一個流。
#include <stdio.h> int fflush(FILE *fp); 返回值:成功返回0,出錯返回EOF |
2.7 記憶體流
標準I/O庫吧資料快取在記憶體中,因此每次一字元和每次一行的I/O更有效。我們也可以通過呼叫setbuf或setvbuf函式讓I/O庫使用我們自己的緩衝區。標註的I/O流,雖然仍使用FILE指標進行訪問,但其實並沒有底層檔案,所有的I/O都是通過在緩衝區與主存之間來回傳送位元組完成的。 #include <stdio.h>
FILE *fmemopen(void *buf, size_t size, const char * type); 返回值:成功返回流指標,出錯返回NULL |
type引數控制如何使用流:
type |
說明 |
r |
為讀而開啟 |
w |
為寫而開啟 |
a |
追加,為在第一個null位元組處寫而開啟 |
r+ |
為讀寫而開啟 |
w+ |
把檔案截斷0長度,為讀寫而開啟 |
a+ |
追加:為在第一個null位元組處讀和寫而開啟 |
這些取值對應基於檔案的標準I/O流的type引數值,但其中有些微笑差別。
(1) 無論何時以追加寫方式開啟記憶體時,當前位置設為緩衝區中的第一個null位元組。如果緩衝區中不存在null位元組,則當前位置就設為緩衝區結尾的後一個位元組。當流不是以追加方式開啟時,當前位置設為緩衝去的開始位置。因為追加寫模式通過第一個null位元組確定資料的尾端,記憶體留並不適合儲存二進位制資料(二進位制資料在資料尾端之前就可能包含多個null位元組)。
(2) 如果buf引數是一個null指標,開啟流進行讀寫沒有任何意義。
(3) 任何時候需要增加緩緩衝區中的數量以及呼叫fclose、fflush、fseek、fseeko和fsetpos時都會在當前位置寫入一個null位元組。