標籤:unix linux 檔案io 緩衝 輸入輸出
標準的I/O是由ANSI C標準規定的,不僅可以在UNIX/Linux上使用,還可以在其他地方使用。
標準I/O庫對應檔案的操作是圍繞流(stream)進行的。當用標準I/O庫開啟或建立檔案時,便使一個流與一個檔案相關聯。標準I/O庫的函數對於檔案的描述是基於FILE對象指標的,該對象是一個結構體,包含了I/O庫管理流所需要的全部資訊:用於實際I/O的檔案描述符、指向流緩衝的指標、緩衝長度、當前在緩衝中的字元數、出錯標誌等等。
標準輸入、標準輸出、標準出錯
和檔案描述符STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO類似,標準I/O也有這三項,它們是通過預定義的指標stdin、stdout、stderr來引用的。這三個指標定義於標頭檔<stdio.h>。
緩衝
眾所周知,I/O操作使比較耗時的。如果每次I/O操作只進行少量讀或寫,那麼I/O操作的次數肯定會很多。緩衝的作用就是把進行I/O操作的資料先存起來,等到存到一定量時再進行實際的I/O操作。系統調用read和write是不帶緩衝的,所以每次調用都會進行I/O操作。標準I/O是帶緩衝的,它會對流自動進行緩衝管理。標準I/O提供以下三種緩衝:
1、全緩衝:指的是在填滿I/O緩衝區後才進行實際的I/O操作。對於磁碟上的檔案通常是由標準I/O實施全緩衝的。在一個流上第一次執行I/O操作時,相關標準I/O函數通常調用malloc開闢使用的緩衝區。
2、行緩衝:指的時在輸入或輸出遇到分行符號時,標準I/O庫執行實際的I/O操作。當流涉及到終端時(如標準輸入、標準輸出),通常使用行緩衝。
3、不帶緩衝:指的是標準I/O庫不對字元進行緩衝儲存,直接調用系統調用。標準出錯stderr通常是不帶緩衝的,這樣就可以使得錯誤資訊儘快顯示出來。
這裡還要提到一個概念:沖洗(flush),指的是清空緩衝區,緩衝區資料進行I/O操作。調用函數fflush就可以沖洗一個流。
可以通過函數更改緩衝類型:
#include<stdio.h>
void setbuf( FILE *restrict fp, char *restrict buf);
int setvbuf( FILE *restrict fp, char *restrict buf, int mod, size_t size);
沖洗流使用:
#include<stdio.h>
int fflush(FILE *fp);
如果fp為NULL,則所有輸出資料流被沖洗。
開啟流
#include<stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *fopen(const cahr *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int filedes, const char *type);
關閉流
#include<stdio.h>
int fclose(FILE *fp);
讀寫流
流開啟之後,可以在三種不同類型的非格式化I/O(還有格式化的I/O)中進行選擇,對其進行讀寫操作:
1、每次一個字元的I/O。每次讀或寫一個字元,如果流是帶緩衝的,則標準I/O會處理所有緩衝。
2、每次一行的I/O。如果想要一次讀或寫一行,使用fgets或fputs,每次都以一個分行符號終止。
3、直接I/O。fread和fwrite支援這種類型的I/O。每次I/O操作讀或寫某種數量的對象,而這個對象具有指定的長度。這兩個函數常用於從二進位的檔案中每次讀或寫一個結構。
每次讀入一個字元的I/O(函數結尾c說明每次一個字元,char)
#include<stdio.h>
int getc(FILE* fp);
int fgetc(FILE *fp);
int getchar(void);//等價於getc(stdin)
對應於上面,每個輸入函數都有一個輸出函數
#include<stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);//等價於putchar(c,stdout);
每次一行I/O(函數結尾s說明每次一行,string)
#include<stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);//從標準輸入讀
對應於上面,每個輸入函數都有一個輸出函數
#include<stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
二進位I/O
使用二進位I/O常常一次操作一個結構。如果使用getc或puts讀寫一個結構,那麼必須迴圈通過整個結構,每次迴圈處理一個位元組,非常耗時。如果使用fputs或fgets,那麼fputs/fgets遇到null位元組就停止,而結構中可能含有NULL位元組,不能實現讀結構的要求。因此提供了兩個函數執行二進位的I/O.
#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);
二進位I/O的基本問題是:只能用於在同一檔案系統上操作。隨著網路檔案系統系統的興起,這兩個函數的使用就受到限制。
格式化I/O
格式化輸入的4個函數
#include<stdio.h>
int printf(const char *restrict format,……);
int fprintf(FILE *restrict fp, const char *restrict format,……);
int sprintf(char *restrict buf, const char* restrict format, ……);
int sprintf(char *restrict buf, size_t n, const char *restrict format,……);
格式化輸入
#include<stdio.h>
int scanf(const char *restrict format,……);
int fscanf(FILE *restrict fp, const char *restrict format,……);
int sscanf(const char *restrict buf, const char *restrict format,……);
在UNIX系統中,標準I/O庫最終都要調用檔案I/O的系統調用的。每個標準I/O流都有一個與其相關聯的檔案描述符,可以用下面函數來獲得對應的檔案描述符
#incude<stdio.h>
int fileno(FILE *fp);