标准IO库
标准io库就是C语言用来处理流和文件的一个库,它是对文件IO系统调用(不同操作系统提供不同的系统调用)的封装,这使得这套标准库能够隐藏细节,可以移植到其他平台上。
流和对象
对于标准IO库来说,它们的操作是围绕流进行的。标准库将需要操作的文件与流关联,通过流来对文件进行各种操作。当打开一个流时,标准IO函数返回一个FILE对象的文件结构指针,这样就能通过文件结构指针对文件进行操作。
文件结构指针就是一个指向文件对象的指针,文件对象则是将文件所有信息封装为一个对象结构,这样的话能够进行读写字符、字符串、格式化数据、二进制数据多种形式的操作。
缓存
相对于IO系统调用直接操作文件描述符,没有设置缓存,只能读写二进制文件(因为没有设置缓存,每次只能有一个字节被接收变量接收)。IO标准库则设置了缓存,它在内存中开辟一个缓存区。当执行读文件操作时,会先将数据读入缓存区,装满后从缓存区依次读入接收变量。这样使得能够读写字符文件(一个字符多个字节)等多字节文件。
- 缓存类型
标准IO提供三种缓存方式
全缓存:填满缓存区才进行IO操作(标准输入和标准输出不涉及交互,才是全缓存)
行缓存:遇到换行符就执行IO操作
不到缓存:不对字符进行缓存(标准出错是不带缓存)
标准IO库函数
设置缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <stdio.h>
void setbuf(FILE *fp , char *buf);
int setvbuf(FILE *fp , char *buf , int mode , size_t size);
int fflush(FILE *fp);
|
打开和关闭流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include <stdio.h>
FILE *fopen(const char *pathname , const char *type);
FILE *freopen(const char *pathname , const char *type , FILE *fp);
FILE *fdopen(int fd , const char *type);
int fclose(FILE *fp);
|
读和写流
一旦打开了流,可以在三种不同的 非格式化IO 中进行选择,对其进行读写操作。
- 每次一个字符的IO
一次读或者写一个字符,如果流带缓冲,则处理所有缓冲
- 每次一行的IO
使用fgets和fputs一次读或写一行,每行以一个换行符终止。
- 直接IO
fread和fwrite函数支持,每次IO操作读或写某种数量的对象,每个对象具有指定长度。通常用于从二进制文件中读或写一个结构
输入函数
用于一次读一个字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
#include <stdio.h>
int getc(FILE *fp) ;
int fgetc(FILE *fp);
int getchar(void) ;
int ferror(FILE *fp);
int feof(FILE *fp);
void clearerr(FILE *fp);
int ungetc(int c , FILE *fp);
|
输出函数
对应输入函数,每个输入函数都有一个输出函数,性质与上面输入函数相对应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
int putc(int c , FILE *fp);
int fputc(int c , FILE *fp);
int putchar(int c);
|
每次一行IO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
char *fgets(char *buf , int n , FILE *fp);
char *gets(char *buf);
int fputs(const char *str , FILE *fp);
int puts(const char *str);
|
二进制IO
二进制IO,我么需要一次读或写整个结构
fgetc和fputs一次只能读写一字节
其中可能会有null字符,所以不能用fputs和fgets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include <stdio.h>
long ftell(FILE *fp);
int fseek(FILE *fp , long offset , int whence);
void rewind(FILE *fp);
int fgetpos(FILE *fp , fpos_t *pos);
int fsetpos(FILE *fp , const fpos_t *pos);
|
格式化IO
格式化输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <stdio,h>
int printf(const char *format , ...);
int fprintf(FILE *fp , const char *format , ...);
int sprintf(char *buf , const char *format , ...);
#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);
|
格式化输入
1 2 3 4 5 6 7 8 9
| #include <stdio,h>
int scanf(const char *format , ...); int fscanf(FILE *fp , const char *format , ...); int sscanf(const char *buf , const char *format , ...);
|
实现细节
总的来说,标准IO函数库中的库函数最终都会调用IO系统调用
因此,每个流都会有与其相关的文件描述符(IO系统调用都是使用文件描述符来进行的)
我们可以使用函数获取文件描述符
1 2 3 4 5 6 7 8 9
| #include <stdio.h>
int fileno(FILE *fp);
|
我们可以用一个程序来获取标准库的实现细节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include "../../include/standard_error.h" #include <stdio.h>
void pr_stdio(const char * , FILE *fp);
int main(void) { FILE *fp ; fputs("enter any character\n" , stdout); if(getchar() == EOF) { err_sys("getchar error"); } fputs("one line to standard error\n",stderr);
pr_stdio("stdin" , stdin); pr_stdio("stdout" , stdout); pr_stdio("stderr" , stderr);
if((fp = fopen("/etc/motd" , "r")) == NULL) { err_sys("fopen error"); }
if(getc(fp) == EOF) { err_sys("getc error"); }
pr_stdio("/etc/motd" , fp);
exit(0); }
void pr_stdio(const char *name , FILE *fp) { printf("stream = %s, " , name); if(fp->_flags & _IONBF) { printf("无缓存"); }else if(fp->_flags & _IOLBF) { printf("行缓存"); }else { printf("全缓存"); } printf(", buffer size = %d\n" , fp->_bufsiz); }
|
临时文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdio.h>
char *tmpnam(char *ptr);
FILE *tmpfile(void);
char *tempnam(const char *directory , const char *prefix);
|
tmpnam和tmpfile应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include "../../include/standard_error.h" #include <stdio.h>
int main(void) { char name[L_tmpnam] , line[MAXLINE] ; FILE *fp ;
printf("%s\n",tmpnam(NULL));
tmpnam(name); printf("%s\n",name);
if((fp = tmpfile()) == NULL) { err_sys("tmpfile error"); } fputs("one line of output\n",fp); rewind(fp); if(fgets(line , sizeof(line) , fp) == NULL) { err_sys("fgets error"); } fputs(line , stdout); exit(0); }
|
tempnam应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "../../include/standard_error.h" #include <stdio.h>
int main(int argc , char *argv[]) { if(argc != 3) { err_quit("usage:a.out<directory><prefix>"); }
printf("%s\n",tempnam(argv[1][0] != ' ' ? argv[1]:NULL , argv[2][0] != ' '?argv[2]:NULL)); exit(0) ; }
|