- 爲什麼要設計標準I/O庫?
➢ 直接使用API進行文件訪問時,需要考慮許多細節問題,例如:read、write時,緩衝區的大小該如何確定,才能使效率最優。
➢ read和write等底層系統調用函數進行輸入輸出時,在用戶態和內核態之間來回切換,每次讀出或寫入的數據量較少,導致頻繁的I/O操作,增加了系統開銷。
- 標準I/O庫是ANSI C規範的一部分,函數原型在文件stdio.h中定義,對底層I/O系統調用進行了封裝,爲程序員提供了帶有格式轉換功能的輸入輸出操作,並在用戶空間增加了緩衝區管理。
- 文件IO和標準IO區別:
- 文件I/O:文件I/O稱之爲不帶緩存的IO(unbuffered I/O)。不帶緩存指的是每個read,write都調用內核中的一個系統調用。也就是一般所說的低級I/O——操作系統提供的基本IO服務,與os綁定,特定於linix或unix平臺。
- 標準I/O:標準I/O是ANSI C建立的一個標準I/O模型,是一個標準函數包和stdio.h頭文件中的定義,具有一定的可移植性。標準I/O庫處理很多細節。例如緩存分配,以優化長度執行I/O等。標準的I/O提供了三種類型的緩存。
(1)全緩存:當填滿標準I/O緩存後才進行實際的I/O操作。
(2)行緩存:當輸入或輸出中遇到新行符時,標準I/O庫執行I/O操作。
(3)不帶緩存:stderr就是了。
- 文件I/O 又稱爲低級磁盤I/O,遵循POSIX相關標準。任何兼容POSIX標準的操作系統上都支持文件I/O。
- 標準I/O被稱爲高級磁盤I/O,遵循ANSI C相關標準。只要開發環境中有標準I/O庫,標準I/O就可以使用。(Linux 中使用的是GLIBC,它是標準C庫的超集。不僅包含ANSI C中定義的函數,還包括POSIX標準中定義的函數。因此,Linux 下既可以使用標準I/O,也可以使用文件I/O)。
- 通過文件I/O讀寫文件時,每次操作都會執行相關係統調用。這樣處理的好處是直接讀寫實際文件,壞處是頻繁的系統調用會增加系統開銷,標準I/O可以看成是在文件I/O的基礎上封裝了緩衝機制。先讀寫緩衝區,必要時再訪問實際文件,從而減少了系統調用的次數。
- 文件I/O中用文件描述符表現一個打開的文件,可以訪問不同類型的文件如普通文件、設備文件和管道文件等。而標準I/O中用FILE(流)表示一個打開的文件,通常只用來訪問普通文件。
- 標準IO庫
- 分離了應用程序空間和實際的物理設備
- 減少了直接讀盤次數,提高性能
➢ 讀取前查看是否已存在頁緩存中,如果已經存放在了頁緩存中,數據立即返回給應用程序
➢ 寫數據前先寫到頁緩存中,如果用戶採用的是同步寫機制( synchronous writes ), 那麼數據會立即被寫回到磁盤上,應用程序會一直等到數據被寫完爲止;如果用戶採用的是延遲寫機制( deferred writes ),那麼應用程序就完全不需要等到數據全部被寫回到磁盤,數據只要被寫到頁緩存中去就可以了。
- 異步緩存和延遲寫機制一樣,不等數據全部寫回到磁盤,數據只要寫到頁緩存中即可,不同的是當數據全部寫到磁盤上後,異步機制會通知給應用程序,延遲寫機制不會。
fopen函數
- fopen函數功能
打開一個指定文件
- 函數原型
FILE *fopen(const char *restrict pathname, const char *restrict type);
- 參數
pathname:要打開的文件名
type:指定文件的讀、寫方式
- type 說明:
- r或rb 爲讀而打開
- w或wb 使文件長度爲0,或爲寫而創建
- a或ab 添加;爲在文件尾寫而打開,或爲寫而創建
- r+或r+b或rb+ 爲讀和寫而打開
- w+或w+b或wb+ 使文件長度爲0,或爲讀和寫而打開
- a+或a+b或ab+ 爲在文件尾讀和寫而打開或創建
限制 |
r |
w |
a |
r+ |
w+ |
a+ |
文件必須存在 |
√ |
|
|
√ |
|
|
刪除文件以前內容 |
|
√ |
|
|
√ |
|
流可以讀 |
√ |
|
|
√ |
√ |
√ |
流可以寫 |
|
√ |
√ |
√ |
√ |
√ |
流只在尾端處寫 |
|
|
√ |
|
|
√ |
setbuf和setvbuf函數
- setbuf和setvbuf函數功能
打開和關閉緩衝機制
- 函數原型
void setbuf(FILE *steam, char *buf);
void setvbuf(FILE *steam, char *buf, int mode, size_t size);
函數 |
mode |
buf |
緩存及長度 |
緩存的類型 |
setbuf |
|
nonnull |
長度爲BUFSIZ的用戶緩存 |
全緩存 |
setbuf |
|
NULL |
(無緩存) |
不帶緩存 |
setvbuf |
_IOFBF |
nonnull |
長度爲size的用戶緩存 |
全緩存 |
setvbuf |
_IOFBF |
NULL |
合適長度的系統緩存 |
全緩存 |
setvbuf |
_IOLBF |
nonnull |
長度爲size的用戶緩存 |
行緩存 |
setvbuf |
_IOLBF |
NULL |
合適長度的系統緩存 |
行緩存 |
setvbuf |
_IONBF |
忽略 |
無緩存 |
不帶緩存 |
fdopen函數
- fdopen函數功能
取一個現存的文件描述符,並使一個標準I/O流與該描述符相結合
- 頭文件
#include<stdio.h>
- 函數原型
FILE *fdopen(int fd, const char *type);
- fdopen常用於由創建管道及網絡通信通道函數返回的描述符。
➢ 這些特殊類型的文件,不能用fopen打開
➢ 因此必須先調用設備專用函數以獲得一個文件描述符,然後再用fdopen使一個標準I/O流與該描述符相關聯
- 對於fdopen函數,type參數的意義稍有區別
➢ 因爲該描述符已被打開,所以fdopen爲寫而打開並不截短該文件
➢ 不能用於創建該文件(因爲如若一個描述符引用一個文件,則該文件一定已經存在)
Type值 |
操作文件類型 |
是否新建文件 |
是否清空原文件 |
可讀 |
可寫 |
讀寫開始位置 |
r |
文本文件 |
NO |
NO |
YES |
NO |
文件開頭 |
r+ |
文本文件 |
YES |
NO |
YES |
YES |
文件開頭 |
w |
文本文件 |
YES |
YES |
NO |
YES |
文件開頭 |
w+ |
文本文件 |
YES |
YES |
YES |
YES |
文件開頭 |
a |
文本文件 |
NO |
YES |
NO |
YES |
文件結尾 |
a+ |
文本文件 |
NO |
YES |
YES |
YES |
文件結尾 |
rb |
二進制文件 |
NO |
NO |
YES |
NO |
文件開頭 |
r+b或rb+ |
二進制文件 |
YES |
NO |
YES |
YES |
文件開頭 |
wb |
二進制文件 |
YES |
YES |
NO |
YES |
文件開頭 |
w+b或wb+ |
二進制文件 |
YES |
YES |
YES |
YES |
文件開頭 |
ab |
二進制文件 |
NO |
YES |
NO |
YES |
文件結尾 |
a+b或ab+ |
二進制文件 |
NO |
YES |
YES |
YES |
文件結尾 |
FILE *fp;
int fd;
if ((fp = fopen("hello.txt", "w+")) == NULL) {
printf("fopen file error\n");
return 0;}
fprintf(fp, "hello word\n");
fclose(fp);
if ((fd = open("hello.txt", O_RDWR)) == -1) {
printf("open file fail\n");
return 0;}
if ((fp = fdopen(fd, "a+")) == NULL) {
printf("fdopen open\n");
return 0;
}
fprintf(fp, "linux c program");
fclose(fp);