標準IO庫
標準IO庫處理有很多細節,例如緩沖區分配、以優化的塊長度執行IO等。
1.流和FILE對象
對于標準IO庫,所有操作都是圍繞流
進行的,當用標準IO庫打開或創建一個文件時,就使一個流與一個文件相關聯。
流的定向決定讀寫字符是單字節還是多字節字符集(多字節對應寬定向,單字節對應字節定向)。fwide用于設置流的定向:
#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
/* 返回值:寬定向為正值;字節定向為負值;流是未定向,返回0 */
由于fwide無出錯返回,因此在調用fwide前先清除errno,從fwrite返回時,檢查errno的值。
2.緩沖
為了不頻繁使用write和read,標準IO庫提供了緩沖:
- 全緩沖,填滿標準IO緩沖區后進行實際IO操作
- 行緩沖,遇到換行符或者填滿行緩沖區后進行操作
- 不緩沖,不緩沖,直接輸出
通過下列兩個函數可以對緩沖類型進行修改:
#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf );
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
/* 返回值:成功返回0,失敗返回非0 */
使用mode
參數說明所需的緩沖類型:
mode參數 | 類型 |
---|---|
_IOFBF | 全緩沖 |
_IOLBF | 行緩沖 |
_IONBF | 不緩沖 |
若不使用緩沖,則忽略buf
和size
參數。
3.打開流
下列3個函數用于打開流:
#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 */
三者區別在于:
-
fopen 打開路徑名為
pathname
的一個指定文件 - freopen 在一個指定的流上打開一個指定的文件,若該流已經打開,則先關閉該流。若該流已經定向,則使用freopen清除該定向。此函數一般用于將指定文件打開為一個預定義的流:標準輸入,輸出和錯誤
- fdopen取一個已有的文件描述符(可能從open,dup,pipe,socket得到此描述符),并使一個標準IO流與該描述符向結合。此函數常用于創建管道和網絡通信管道函數返回的描述符。
使用fclose可以關閉一個打開的流,在文件被關閉前,沖洗緩沖中的輸出數據,并丟棄緩沖區中任何輸入數據,并且釋放此緩沖區。
一個進程正常終止時,則所有帶未寫緩沖數據的標志IO都會被沖洗,所有打開的標準IO流都被關閉。
4.讀和寫流
打開流之后,可以對其進行讀寫操作。
輸入函數
以下3個函數用于一次讀一個字符:
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
/* 返回值:成功返回下一個字符;若到達尾端或者出錯返回EOF */
getchar等同于getc(stdin)。前兩個函數區別在于,getc可被實現為宏,而fgetc不能,這就意味著:
- getc的參數不應當具有副作用的表達式
- fgetc一定是個函數,可以通過函數指針傳遞給另一個函數
- 調用fgetc所需時間可能比getc更長
輸出函數
對應輸入函數都有一個輸出函數:
#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可以被實現為宏。
5.每次一行IO
下列兩個函數提供每次輸入一行的功能:
#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
/* 返回值:成返回buf;若到達尾端或者出錯返回NULL*/
盡量使用fgets防止緩沖區溢出。
對應輸出一行的功能:
#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
/* 返回值:成功返回非負值,出錯返回EOF*/
fputs將一個null
字節終止的字符串寫到指定的流,puts除了null
,隨后添加了一個換行符到標準輸出。
如果總是使用fgets和fputs,那么久會熟知在每行終止處我們必須字節處理換行符。
6.二進制IO
前面的輸入函數只能一次性讀取一個字節或者一行數據,為了獲得整個結構,提供了下列兩個函數用以執行二進制IO操作:
#include <stdio.h>
size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
讀寫一個結構體,可以如下:
struct {
short count;
long total;
char name[NAMESIZE];
} item;
if (fwrite(&item, sizeof(item), 1, fp) != 1)
err_sys("fwrite error");