标准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
本章基本就是这些内容了。