1. 程式人生 > >Unix/Linux程式設計-標準I/O庫

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)
fdopen取一個已有的檔案描述符,並使一個標準的I/O流與該描述符相結合。常用於建立管道和網路通訊通道返回的描述符,因為這次特殊的檔案不能用標準I/O函式開啟。
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);

fseek中的whence與lseek函式相同。分別是SEEK_SET、SEEK_CUR和SEEK_END。

2.5 關閉流

#include <stdio.h>

int  fclose(FILE *fp);

返回值:成功返回0,出錯返回EOF

在該檔案被關閉之前,沖洗緩衝中的輸出資料。緩衝區中的任何輸入資料被丟棄。如果標準I/O庫已經為該流自動分配一個緩衝區,則釋放此緩衝區。
當一個程序中止時(直接呼叫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

使用setbuf函式開啟或關閉緩衝機制。為了帶緩衝進行I/O,引數buf必須指向一個長度為BUFSIZE的緩衝區(該常量定定義在<stdio.h>中)。通常在此之後該流就是全緩衝,但如果該流與一個終端裝置相關,那麼可能將其設定為行緩衝。如果要關閉緩衝,將buf設定為NULL。
使用setvbuf函式,我們可以精確地說明所需的緩衝型別。這是用mode引數實現的:

mode

說明

_IOFBF

全緩衝

_IOLBF

行緩衝

_IONBF

不到緩衝

如果指定一個不帶緩衝的流,則忽略buf和size引數。如果指定全緩衝或行緩衝,則buf和size可選擇地指定一個緩衝區及其長度。如果該流是帶緩衝的,而buf是NULL,則標準I/O庫將自動地為該流分配適當長度的緩衝區。適當長度指的是用常量BUFSIZ所指定的值。

任何時候,我們都可以強制沖洗一個流。

#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

函式允許呼叫者提供緩衝區用於記憶體流;buf引數指向緩衝區的開始位置,size指定了緩衝區大小的位元組數。如果bug引數為空,fmemopen函式分配size位元組數的緩衝區,當流關閉時緩衝區會被釋放。
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位元組。