Linux 系统应用编程——标准I/O
標準I/O的由來
??????? 標準I/O指的是ANSI C 中定義的用于I/O操作的一系列函數。
????????只要操作系統安裝了C庫,標準I/O函數就可以調用。換句話說,如果程序中使用的是標準I/O函數,那么源代碼不需要任何修改就可以在其他操作系統下編譯運行,具有更好的可移植性。
????????除此之外,使用標準I/O可以減少系統調用的次數,提高系統效率。標準I/O函數在執行時也會用到系統調用。在執行系統調用時,Linux必須從用戶態切換到內核態,處理相應的請求,然后再返回到用戶態。如果頻繁的執行系統調用會增加系統的開銷。為避免這種情況,標準I/O在使用時為用戶控件創建緩沖區,讀寫時先操作緩沖區,在合適的時機再通過系統調用訪問實際的文件,從而減少了使用系統調用的次數。
流的含義
????? 標準I/O的核心對象就是流。當用標準I/O打開一個文件時,就會創建一個FILE結構體描述該文件(或者理解為創建一個FILE結構體和實際打開的文件關聯起來)。我們把這個FILE結構體形象的稱為流,我們在stdio.h里可以看到這個FILE結構體。
[cpp]?view plaincopy這個結構體:1)對 fd 進行了封裝;2)對緩存進行了封裝 unsigned char *buffer; 這而指向了buffer 的地址,實際這塊buffer是cache,我們要將其與用戶控件的buffer分開。
標準I/O函數都是基于流的各種操作,標準I/O中的流的緩沖類型有下面三種:
1)、全緩沖。?
在這種情況下,實際的I/O操作只有在緩沖區被填滿了之后才會進行。對駐留在磁盤上的文件的操作一般是有標準I/O庫提供全緩沖。緩沖區一般是在第一次對流進行I/O操作時,由標準I/O函數調用malloc函數分配得到的。
術語flush描述了標準I/O緩沖的寫操作。緩沖區可以由標準I/O函數自動flush(例如緩沖區滿的時候);或者我們對流調用fflush函數。
2)、行緩沖
在這種情況下,只有在輸入/輸出中遇到換行符的時候,才會執行實際的I/O操作。這允許我們一次寫一個字符,但是只有在寫完一行之后才做I/O操作。一般的,涉及到終端的流--例如標注輸入(stdin)和標準輸出(stdout)--是行緩沖的。
3)、無緩沖
標準I/O庫不緩存字符。需要注意的是,標準庫不緩存并不意味著操作系統或者設備驅動不緩存。
?
標準I/O函數時庫函數,是對系統調用的封裝,所以我們的標準I/O函數其實都是基于文件I/O函數的,是對文件I/O函數的封裝,下面具體介紹·標準I/O最常用的函數:
一、流的打開與關閉
?????? 使用標準I/O打開文件的函數有fopen() 、fdopen() 、freopen()。他們可以以不同的模式打開文件,都返回一個指向FILE的指針,該指針指向對應的I/O流。此后,對文件的讀寫都是通過這個FILE指針來進行。
fopen函數描述如下:
| 所需頭文件 | #include <stdio.h> |
| 函數原型 | FILE *fopen(const char *path, const char *mode); |
| 函數參數 | path: 包含要打開的文件路徑及文件名 mode:文件打開方式 |
| 函數返回值 | 成功:指向FILE的指針 失敗:NULL |
mode用于指定打開文件的方式。
關閉流的函數為fclose(),該函數將流的緩沖區內的數據全部寫入文件中,并釋放相關資源。
fclose()函數描述如下:
| 所需頭文件 | #include <stdio.h> |
| 函數原型 | int fclose(FILE *stram); |
| 函數參數 | stream:已打開的流指針 |
| 函數返回值 | 成功:0 失敗:EOF |
?
二、流的讀寫
1、按字符(字節)輸入/輸出
字符輸入/輸出函數一次僅讀寫一個字符。
字符輸入函數原型如下:
| 所需頭文件 | #include <stdio.h> |
| 函數原型 | int? getc(FILE *stream); int? fgetc(FILE *stream); int? getchar (void); |
| 函數參數 | stream:要輸入的文件流 |
| 函數返回值 | 成功:讀取的字符 失敗:EOF |
?函數getchar等價于get(stdin)。前兩個函數的區別在于getc可被實現為宏,而fgetc則不能實現為宏。這意味著:
1)getc 的參數不應當是具有副作用的表達式。
2)因為fgetc一定是一個函數,所以可以得到其地址。這就允許將fgetc的地址作為一個參數傳給另一個參數;
3)調用fgetc所需時間很可能長于調用getc,因為調用函數通常所需的時間長于調用宏。?
? ? 這三個函數在返回下一個字符時,會將其unsigned char 類型轉換為int類型。說明為什么不帶符號的理由是,如果是最高位為1也不會使返回值為負。要求整數返回值的理由是,這樣就可以返回所有可能的字符值再加上一個已出錯或已達到文件尾端的指示值。在<stdio.h>中的常量EOF被要求是一個負值,其值經常是-1。這就意味著不能將這三個函數的返回值存放在一個字符變量中,以后還要將這些函數的返回值與常量EOF相比較。
? ? 注意,不管是出錯還是到達文件尾端,這三個函數都返回同樣的值。為了區分這兩種不同的情況,必須調用ferror或feof。
[cpp]?view plaincopy
在大多數實現中,為每個流在FILE對象中維持了兩個標志:
出錯標志。
文件結束標志。
?字符輸出-函數原型如下:
| 所需頭文件 | #include <stdio.h> |
| 函數原型 | int putc (int c ,FILE *stream); int fputc (int c, FILE *stream); int putchar(int c); |
| 函數返回值 | 成功:輸出的字符c 失敗:EOF |
putc()和fputc()向指定的流輸出一個字符(節),putchar()向stdout輸出一個字符(節)。
2、按行輸入、輸出
? ??? 行輸入/輸出函數一次操作一行。
行輸入函數原型如下:
| 所需頭文件 | #include <stdio.h> |
| 函數原型 | char *gets(char *s); char ?*fgets(char *s,int size,FILE *stream); |
| 函數參數 | s:存放輸入字符串的緩沖區首地址; size:輸入的字符串長度 stream:對應的流 |
| 函數返回值 | 成功:s 失敗或到達文件末尾:NULL |
這兩個函數都指定了緩沖區的地址,讀入的行將送入其中。gets從標準輸入讀,而fgets則從指定的流讀。
gets函數容易造成緩沖區溢出,不推薦使用;
fgets從指定的流中讀取一個字符串,當遇到 \n 或讀取了 size - 1個字符串后返回。注意,fgets不能保證每次都能讀出一行。 如若該行(包括最后一個換行符)的字符數超過size -1 ,則fgets只返回一個不完整的行,但是,緩沖區總是以null字符結尾。對fgets的下一次調用會繼續執行。
行輸出函數原型如下:
| 所需頭文件 | #include <stdio.h> |
| 函數原型 | int puts(const char *s); int fgets(const char *s,FILE *stream); |
| 函數參數 | s:存放輸入字符串的緩沖區首地址; stream:對應的流 |
| 函數返回值 | 成功:非負值 失敗或到達文件末尾:NULL |
函數fputs將一個以null符終止的字符串寫到指定的流,尾端的終止符null不寫出。注意,這并不一定是每次輸出一行,因為它并不要求在null符之前一定是換行符。通常,在null符之前是一個換行符,但并不要求總是如此。
下面舉個例子:模擬文件的復制過程:
[cpp]?view plaincopy執行結果如下:
[cpp]?view plaincopy我們可以看到,這里將time.c拷貝給1.c ,1.c和time.c大小一樣,都是437個字節;
3、以指定大小為單位讀寫文件
三、流的定位
四、格式化輸入輸出
這里舉個相關應用例子:循環記錄系統時間
實驗內容:程序每秒一次讀取依次系統時間并寫入文件
[cpp]?view plaincopy執行結果如下:
[cpp]?view plaincopy總結
以上是生活随笔為你收集整理的Linux 系统应用编程——标准I/O的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UITableView的优化原理
- 下一篇: 77GHz毫米波雷达快速chirp信号技