標準I/O庫主要是由ANSI C實現的。主要是爲了在不同操作系統上實現。
在UNIX中,標準I/O庫的最終都要調用I/O例程。
標準I/O庫的操作都是圍繞stream來進行的。與流相對應的是FILE對象的指針,這個FILE對象的指針和file描述符是有差別的,這是一個結構體,定義在stdio.h中,不同於file描述符就是一個int型數據。
預定義的stream是有三個:stdin,stdout,stderr。對應的文件描述符分別是:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。
緩存分三種:_IOFBF,_IOLBF,_IONBF,全緩存,行緩存,沒有緩存。默認的,stdin和stdout是行緩存,stderr是不帶緩存。緩存類型是可以設置的:
#include <stdio.h>
void setbuf(FILE* fp, char* buf);
void setvbuf(FILE* fp, char* buf, int mode, size_t size);
其中,setbuf有buf來決定是否帶緩存,而setvbuf是通過mode來決定是哪一種緩存,而buf來決定是用戶緩存還是系統緩存,如果是系統緩存,則size是沒有意義的。系統緩存的size是由系統決定的,可能是st_blksize,也可能是BUFSIZE。這個由系統來決定。
流可以直接刷新,可以強制刷出:
#include <stdio.h>
int fflush(FILE* fp);
一個通用的過程應該是:打開---->讀寫----->關閉,其他還有 定位, 格式化I/O, 臨時文件。
打開:
#include <stdio.h>
FILE* fopen(const char* pathname, const char* type);
FILE* freopen(const char* pathname, const char* type, FILE* fp);
FILE* fdopen(int filedes, const char* type);
其中,freopen 在一個特定的流上打開一個指定的文件。此函數一般用於將一個指定的文件打開爲一個預定以的流:stdin,stdout,stderr。
fdopen則是把filedes轉爲FILE*。
type是規定讀寫方式:r w a +
b在unix內核中不起作用,因爲內核不區分文本文件和2進制文件。
對應的關閉:
int fclose(FILE* fp);
正常終止進程,流會被flush,並關閉。
讀寫分爲三種:
1,字符讀寫:
#include <stdio.h>
int getc(FILE* fp);
int fgetc(FILE* fp);
int getchar(void);
int putc(int c, FILE* fp);
int fputc(int c, FILE* fp);
int putchar(int c);
其中,getchar是getc的特殊形式,fp是stdin。putchar是putc的特殊形式,fp是stdout.
getc和putc可以用宏實現,所以會快一些。而fgetc和fputc可以作爲函數參數。
getc和fgetc是返回值在出錯和文件結尾的時候都返回EOF,可以用如下函數判斷:
int ferror(FILE* fp);
int feof(FILE* fp);
清除標誌用:
int clearerr(FILE* fp);
獲取字符可以回送,取出來看一下,然後送回去,想用的話在get:
int ungetc(int c, FILE* fp);
EOF不能回送。
2 行讀寫:
#include <stdio.h>
char* fgets(char* buf, int n, FILE* fp);
char* gets(char* buf);
char* fputs(const char* str, FILE* fp);
char* puts(const char* str);
其中,gets可能會出現緩存越界。fgets讀取n-1個字符。
fputs將一個以null符終止的字符串寫到指定的流,null 不寫出。
3 二進制讀寫:
這個主要是讀取一個結構體(包括數組)之類的完整數據。
#include <stdio.h>
size_t fread(void* ptr, size_t size, size_t nobj, FILE* fp);
size_t fwrite(const void* ptr, size_t size, size_t nobj, FILE* fp);
跨系統很難工作。
打開-->讀寫-->關閉,然後就是定位:
1 unix系統老的方法:
#include <stdio.h>
long ftell(FILE* fp);
int fseek(FILE* fp, long offset, int whence);
void rewind(FILE* fp);
其中,rewind定位到0;
whence可以是: SEEK_SET,SEEK_CUR,SEEK_END.
事實上,文本文件不能單純的用long值來表示。
2 ANSI C引入的:
#include <stdio.h>
int fgetpos(FILE* fp, fpos_t* pos);
int fsetpos(FILE* fp, const fpos_t* pos);
格式化I/O:
#include <stdio.h>
int printf(const char* format, ......);
int fprintf(FILE* fp, const char* format, ......);
int sprintf(char* buf, const char* format, ......);
int scanf(const char* format, ......);
int fscanf(FILE* fp, const char* format, ......);
int sscanf(const char* buf, const char* format, ......);
其中,sprintf可能會越界,必須有調用者來控制。
輸出有一個變種如下:
#include <stdio.h>
#include <stdarg.h>
int vprintf(const char* format, va_list arg);
int vfprintf(FILE* fp, const char* format, va_list arg);
int vsprintf(char* buf, const char* format, va_list arg);
之前有將filedes轉爲FILE*的,
FILE* fdopen(int filedes, const char* type);
反之:
int fileno(FILE* fp);
臨時文件:
#include <stdio.h>
char* tmpnam(char* ptr);
FILE* tmpfile(void);
其中,tmpnam中ptr和返回值是一致的。ptr如果是null,再次調用該函數時是會重寫原先的文件名的,所以如果想保存是不能僅保存指針的。
變種:
char* tempnam(const char* directory, const char* prefix);
臨時文件的目錄有一個順序,最好就直接用這個函數規定就好了。
TMPDIR>directory>P_tmpdir>/tmp
本章基本就是這些內容了。