標準IO函數庫隱藏了buffer大小和分配的細節,使得咱們能夠不用關心預分配的內存大小是否正確的問題。緩存
雖然這使得這個函數庫很容易用,可是若是咱們對函數的原理不熟悉的話,也容易遇到不少問題。網絡
前面的章節中,IO集中在文件描述符,每個打開的文件都對應一個文件描述符,經過文件描述符對文件進行操做。app
如今使用了標準IO庫,討論的重點集中在流(streams)。ide
簡要了解一下流:函數
只有兩個函數能夠修改流的orientation:測試
fwide函數聲明:spa
#include <stdio.h>3d
#include <wchar.h>指針
int fwide(FILE* fp, int mode);rest
函數返回值:
mode取值的不一樣決定函數fwide的不一樣的行爲:
當咱們打開一個流,函數fopen返回一個指向FILE對象的指針。FILE對象一般是一個結構體,包含全部控制流所須要的信息,包括:
緩存(buffering)的做用是爲了儘量少地調用read和write系統調用。
標準IO庫提供三種類型的buffering:
徹底緩存(Fully buffered):在這種緩存機制中,實際的IO操做發生在緩存被寫滿時。正在寫入硬盤的文件被徹底緩存在buffer中。緩存空間每每在第一次IO操做時經過調用malloc函數獲取;
行緩存(Line buffered):在這種緩存機制中,實際的IO操做發生在新的一行字符被讀入或者輸出時,因此容許每一次只輸出一個字符。行緩存有兩點須要注意:buffer的大小是固定的,因此即便當前行沒有讀入或輸出結束,依然可能發生實際的IO,當buffer被寫滿時;一旦有輸入(從無緩存流或者行緩存流中輸入)發生,因此已在buffer中緩存的輸出流都會被馬上輸出(flush)。
flush:標準IO緩存中內容馬上寫入硬盤或者輸出。在終端設備中,flush的做用也多是丟棄緩存中得數據。
無緩存(Unbuffered):不緩存輸入或輸出內容。例如,若是咱們使用fputs函數輸出15個字符,那麼咱們但願這15個字符儘量快地被打印出來。如標準錯誤輸出就要求是無緩存輸出。
ISO C標準要求下面的緩存特性:
上面的標準顯然沒有具體說明各類狀況,通常來講:
咱們可使用函數setbuf和setvbuf函數更改流的緩存機制。
函數聲明:
#include <stdio.h>
void setbuf(FILE* restrict fp, char* restrict buf);
int servbuf(FILE *restrict fp, char* restrict buf, int mode, size_t size);
函數返回值:
這些函數必須在流打開以後,其餘流操做執行以前被調用。
函數做用:
setbuf能夠打開或關閉緩存,打開緩存時,buf指向一個大小爲BUFSIZ(stdio.h中定義的宏)的buffer,一般打開的時徹底緩存,若是當前流關聯的是終端設備,有的系統也會使用行緩存;
servbuf能夠指定打開哪一種類型的緩存。mode的參數能夠取以下的值,若是指定爲無緩存,則參數buf和size都會被忽略。
函數行爲總結以下表所示:
一般來講,咱們應該讓系統本身選擇buffer大小並自動分配,這樣標準IO庫會在關閉流時自動釋放該內存。
flush函數。
函數聲明:
#include <stdio.h>
int fflush(FILE *fp);
函數做用:
使得該流的全部緩存中未寫入硬盤的數據傳入內核中。
一種特殊狀況是,若是fp爲NULL,fflush會使得全部緩存的數據都被flush。
函數fopen、freopen和fdopen函數用來打開一個標準輸入輸出流。
函數聲明:
#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);
函數細節:
參數type取值以下表所示,一共有15種取值,有得取值做用相同:
表格說明:
當打開一個流對文件進行讀寫時,有兩個限制:
六種方式打開一個流總結以下表所示:
須要注意的一點是,當以w和a模式建立一個新文件時,並不能像open或create函數同樣指定文件的權限標誌位。
一種解決方法是經過調整咱們的umask。
打開的流默認的是徹底緩存,若是該流關聯的是終端設備,則是行緩存。
像以前提到的那樣,咱們打開了一個流,並在其餘操做以前,能夠調用setbuf或setvbuf函數修改緩存方式。
關閉流
函數聲明:
#include <stdio.h>
int fclose(FILE* fp);
函數細節,關閉流以前:
當咱們打開一個流,咱們有三種讀寫方式可供選擇:
函數聲明:
#include <stdio.h>
int getc(FILE* fp);
int fgetc(FILE* fp);
int getchar(void);
函數返回值:
函數細節:
函數聲明:
#include <stdio.h>
int ferror(FILE* fp);
int feof(FILE* fp); // Both return: nonzero(true) if condition is true, 0(false) otherwise
void clearerr(FILE* fp);
在大多的實現中,FILE對象中會維護兩個flag:
這兩個flag均可以經過調用clearerr清空。
讀取一個流後,咱們能夠調用函數ungetc壓回讀出來的字符。
函數聲明:
#include <stdio.h>
int ungetc(int c, FILE* fp);
函數返回值:c if OK, EOF on error
函數細節:只支持單個個字符的壓回。
使用場景:
壓回操做常使用在下面的場景:對於一個輸入流,咱們須要根據下一個字符來判斷該如何處理當前的字符。
輸出函數和咱們討論過的輸入函數一一對應,再也不贅述。
函數聲明:
#include <stdio.h>
int putc(int c, FILE* fp);
int fputc(int c, FILE* fp);
int putchar(int c);
函數fgets和gets提供了逐行輸入功能。
函數聲明:
#include <stdio.h>
char *fgets(char* restrict buf, int n, FILE* restrict fp);
char *gets(char* buf);
函數細節:
函數fputs和puts提供了逐行輸出的功能。
函數聲明:
#include <stdio.h>
int fputs(const char* restrict str, FILE* restrict fp);
int puts(const char* str);
函數細節:
比較標準:
將必定量的數據從標準輸入拷貝到標準輸出,計算這一過程所須要的
Code:
使用getc和putc的版本:
#include "apue.h"
int
main(void)
{
int c;
while ((c = getc(stdin)) != EOF)
if (putc(c, stdout) == EOF)
err_sys("output error");
if (ferror(stdin))
err_sys("input error");
exit(0);
}
使用fgets和fputs的版本:
#include "apue.h"
int
main(void)
{
char buf[MAXLINE];
while (fgets(buf, MAXLINE, stdin) != NULL)
if (fputs(buf, stdout) == EOF)
err_sys("output error");
if (ferror(stdin))
err_sys("input error");
exit(0);
}
測試數據:95.8M 3百萬行
測試結果(和第三章中的數據進行了對比,以前跳過了該章節,能夠自行查看一下):
結果說明:
標準IO函數庫分爲兩篇來介紹,本篇是第一篇,主要介紹了
參考資料:
《Advanced Programming in the UNIX Envinronment 3rd》