UNIX高級編程總結-----標準I/O庫 (一 流、緩衝、打開流、讀寫流)

一、流 和 FILE對象

        之前在文件I/O中提到的函數,都是圍繞文件描述符的,當打開一個文件時,返回的是一個文件描述符,然後對該文件描述符進行後續的I/O操作。而對於標準I/O庫,他們的操作是圍繞着 流 進行的。當用標準I/O庫打開或創建一個文件時,我們就使一個流與一個文件相關聯。

        在標準IO流中預定義了3個文件指針,stdin(標準輸入)、stdout(標準輸出)、stderr(標準錯誤)。這三個文件指針都定義<stdio.h>中。

        對於ASCII字符集,一個字符用一個字節來表示。對於國際字符集而言,一個字符可用多個字符集來表示。標準I/O文件流可用於單字節或多字節(俗稱"寬")字符集

        流的定向,決定了所讀所寫的字符是單字節的還是多字節的。當一個流剛被創建出來時,是沒有定向的,如果在一個沒有定向的流上使用一個多字節的I/O函數,則將該流設置爲寬定向,具體多寬由使用的多字節IO函數定。有兩個函數可以改變流的定向:

(1) fwide函數

#include<stdio.h>

#include<wchar.h>

int fwide(FILE *fp, int mode);

                   返回值:若流是寬定向的,返回正值;若流是字節定向的,返回負值;若流是爲定向的,返回0 

 

        注意:

        fwide並不改變以定向流的定向。還應注意的是,fwide無出錯返回。試想一下如果流是無效的會發生什麼呢?我們唯一依靠的就是fwide前先清除errno,從fwide返回時檢查errno的值。

(2) freopen函數

        該函數的解釋,可見   三、打開流

二、緩 衝

1、標準IO庫提供緩衝的目的是,儘可能少的使用read和write調用次數。標準IO的底層實現是運用了read和write,通過緩衝區將要寫入或者讀出的數據先放到緩衝區中,然後達到一定的條件後(比如:緩衝區滿、遇見換行符 等等)去調用一次read或是write。

2、標準的IO提供了三種類型的緩衝

        (1)全緩衝:

        在填滿標準的IO緩衝區後才進行實際的IO操作。對於駐留在磁盤上的文件通常是有標準IO庫實施全緩衝的。在一個流上執行第一次操作時,相關的標準IO函數通常是調用malloc去獲得所需的緩衝區。

        該類型的緩衝區會在 緩衝區填滿時 以及  調用fflush()沖刷流時執行一次寫操作。

        (2)行緩衝:

        在遇到換行符時,執行一次IO操作。對於行緩衝有兩個限制,

        第一,因爲標準IO庫用來收集每一行的緩衝區的長度是固定的,所以只要填滿了緩衝區,那麼即使沒有寫一個換行符也執行一次IO操作。

        第二,任何時候只要通過標準IO庫要求從一個不帶緩衝的流,或者是一個行緩衝的流(它從內核請求需要的數據)得到輸入數據,那麼就會沖洗所有的行緩衝輸出流。對於行緩衝的流後面的括號中的說明,着重解釋一下:

        從不帶緩衝的流中輸入需要從內核中獲取數據。

        從行緩衝的流中得到輸入數據,所需的數據可能還在緩衝區中,不需要從內核中讀數據。

        (3)不帶緩衝:

        不對字符進行緩衝存儲。比如:stderr

        ISO C中要求緩衝有一下幾項特徵:

        a、當且僅當標準輸入和標準輸出,並不指向交互式設備時,他們纔是全緩衝。當指向交互設備時,是行緩衝還是無緩衝都視情況而定,很多系統在指向交互設備後,默認標準錯誤無緩衝。

        b、標準錯誤永遠不會是全緩衝。

 

        對任意一個給定的流,若不喜歡他的默認緩衝類型,可以通過下列函數去進行修改緩衝類型。

#include<stdio.h>

void setbuf(FILE *restrict fp, char *restrict buf);

void setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

                   返回值:成功,返回0;失敗,返回,非0 

        void setbuf(FILE *restrict fp, char *restrict buf);

        可以使用 setbuf 去打開或關閉緩衝機制,參數buf不需指向一個長度爲 BUFSIZ 的緩衝區,設置之後該流的緩衝區爲全緩衝。如果要關閉緩衝機制,則將 buf 爲NULL。

#include <stdio.h>
char outbuf[50];
#if 0
int main(void)
{
    /* 將outbuf與stdout輸出流相連接 */
    setbuf(stdout,outbuf);
    /* 向stdout中放入一些字符串 */
    puts("This is a test of buffered output.");
    puts("This output will go into outbuf");
    puts("and won't appear until the buffer");
    puts("fills up or we flush the stream.\n");
    /* 以下是outbuf中的內容 */
//    puts(outbuf);
    /*刷新流*/
//    fflush(stdout);
    return 0;
}
#else
 
#include<stdio.h>
#include<stdlib.h>
//char buf[6];
int main()
{
    // static char buf[6];
    setbuf(stdout,malloc(100));
    int c;
 
    int i = 0;
    while((c=getchar())!=EOF && getchar())
    {
        putchar(c);
#if 1
        printf("hello\n");
        i++;
        if(i>5)  
        {  
            return 0;  
        }  
#endif  
    }  
    return 0;  

#endif     

       void setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

       參數:stream :指向流的指針 ;
             buf       : 期望緩衝區的地址;
             type     : 期望緩衝區的類型:
                    _IOFBF(全緩衝):當緩衝區爲空時,從流讀入數據。或者當緩衝區滿時,向流寫入數 據。
                    _IOLBF(行緩衝):每次從流中讀入一行數據或向流中寫入一行數據。
                    _IONBF(無緩衝):直接從流中讀入數據或直接向流中寫入數據,而沒有緩衝區。
             size     : 緩衝區內字節的數量。
  注意:意思是這個函數應該在打開流後,立即調用,在任何對該流做輸入輸出前
 

        下面是這兩個函數,參數設置之後其得到的緩衝類型的表:

補充說明一個常見C語言關鍵字:

        restrict

三、打開流 

        

#include<stdio.h>

FILE* fopen(const char *restrict pathname, char *restrict type);

FILE* freopen(const char *restrict pathname, char *restrict type, FILE* restrict fp);

FILE* fdopen(int fd, const char *type);

                   返回值:成功,返回文件指針;失敗,返回NULL 

    (1) fopen 打開路徑執行一個文件

    (2) freopen 在一個制定的流(三參)上打開一個文件(一參),如果該流打開了,那就先關閉之前的流,然後再打開新的文件。如果該流已定向,則 freopen 清除該定向。通常該函數將一個指定的文件打開爲一個預定義的流,標準輸入、標準輸出、標準錯誤。 

    (3) fdopen 這個函數比較有用。將一個文件描述符(open,dup,管道、socket等)與一個標準IO中的流相結合。此函數用於創建管道和網絡通信通道函數返回的描述符。因爲這些特殊的文件描述符不能用標準IO函數fopen打開,所以必須使用該函數將其文件描述符與流相結合。

    這三個都有一個標準IO流的參數,其解釋如下:

注意1:在 UNIX 環境下,UNIX內核對二進制文件 和 文本文件不做區分,故 b 選項沒有意義。

注意2:還有以下在使用fdopen時的注意事項:

注意3:還有注意的一點就是,用 w 或是 a 創建一個新文件時,無發說明該文件的訪問權限位。文件IO中,open和creat都能做到。如果要用標準IO用權限位集來創建文件時,必須使用umask函數。在這裏需要插入一個文件和目錄的知識點umask函數:

include<sys/stat.h>

mode_t umask(mode_t cmask); 

                 返回值:之前的文件模式創建屏蔽字 

    在Linux中設置umask值以及umask函數

注意4: 流引用的是終端設備,該流屬於行緩衝;按系統默認情況下,流被打開是全緩衝。

 

四、讀寫流

        在打開流之後,有三種不同類型的非格式化IO進行讀寫操作:

        (1) 每次一個字符的IO。一次的讀或寫一個字符,如果流帶緩衝,則標準IO函數處理所有的緩衝。

        (2) 每次一行的IO。讀寫以換行符結尾,主要函數有 fputs 和 fgets .

        (3) 直接IO。每次的IO操作讀或寫某種數量的對象,而每個對象具有指定的長度。常用於從二進制文件中每次讀寫一個結構等,主要的函數 fread 和 fwrite。

    1、用於一次只讀一個字符的函數(總共三個函數)

include<stdio.h>

int getc(FILE *fp);

int fgetc(FILE *fp);

int getchar(void); 

                 返回值:成功返回下一個字符;

                                若文件已到達文件尾端或出錯,返回EOF

        函數 getchar  等同於  getc(stdin); getc 可被定義爲宏,而fgetc不能實現爲宏,其解釋引用書中原文:

注意:

       

        在這三個函數中,他們無論 出錯 還是 到文件尾端其返回的都是EOF,爲了區分這兩種情況,引入了兩個函數:

include<stdio.h>

int ferror(FILE *fp);

int feof(FILE *fp); 

                 返回值:條件爲真,返回非0;條件爲假,返回0

void clearerr(FILE *fp);

他的區分原理是,如下:

 

        從流中讀取的數據,可以通過 ungetc 將字符再壓回去;

#include<stdio.h>

int ungetc(int c,FILE* fp);

                               返回值:若成功,返回c;若出錯,返回EOF

 ungetc 該函數實現並不是在底層文件或是設備上執行的,而是在標準IO的緩衝區中操作的。以下是書中全部的解釋:

      以上是介紹了每次單個字符的讀操作,以下是寫操作,也是三個函數

include<stdio.h>

int putc(int c, FILE *fp);

int fputc(int c, FILE *fp);

int putchar(int c); 

                 返回值:成功返回c;若出錯,返回EOF

 putchar(c)  等同於  putc(c, stdout)  ;putc可被實現爲宏,而 fputc 不能實現爲宏。

 

 

文章引用:setbuf 和setvbuf 簡單介紹

下一篇

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