标准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) ; }
   |