标准I/O库由ISO C标准说明,很多操作系统都实现了标准I/O库。
标准I/O库处理很多细节,如缓冲区分配、以优化的块长度执行I/O等。这些处理使用户不必担心如何选择使用正确的块长度。
与文件I/O的区别:
文件I/O | 标准I/O | |
---|---|---|
标准输入 | STDIN_FILENO | stdin |
标准输 | STDOUT_FILENO | stdout |
标准错误 | STDERR_FILENO | stderr |
打开文件 | open | fopen\freopen\fdopen 打开方式(b byte以二进制打开,含 + 读写,含 r 不创建,含 w 截断,含 a 追加) |
创建文件 | creat\open(…,O_CREATE…) | fopen |
关闭文件 | close | fclose |
只读 | O_WRONLY | w\wb |
只写 | O_RDONLY | r\rb |
读写 | O_RDWR | r+ r+b rb+ w+ w+b wb+ |
追加 | O_APPEND | a ab a+ a+b ab+ |
创建 | O_CREATE | 不含 r 都是创建 |
定位 | lseek | ftell\fseek\rewind\ftello\fseeko\fgetpos\fsetpos |
写 | write | fwrite |
读 | read | fread |
/*
流和file对象:
标准I/O库是围绕流进行的,当用标准I/O库打开或创建一个文件时,我们已使一个流与一个文件相关联
当打开一个流时,fopen返回一个指向FILE对象的指针
FILE对象包含了标准I/O库为管理该流所需要的所有信息,包括用于实际I/O的文件描述符、指向用于该流缓冲区的指针、缓冲区的长度、当前缓冲区中的字符数以及出错标志
流定向:
以单字节、多字节读写文件
fwide: fwide并不改变已经定向的流
int fwide(FILE* file,int mode)
file : 打开的文件流指针
mode : 0 未定向,>0 宽定向、多自己定向,<0 单字节定向
返回值:0 未定向,>0 宽定向、多自己定向,<0 单字节定向
标准输入输出、标准错误:
标准流指针:stdin stdout stderr
缓冲:
缓冲是为了尽可能减少read和write调用的次数
标准I/O提供了以下3中类型的缓冲:
1. 全缓冲,缓冲区填满后才进行实际的I/O操作
2. 行缓冲,输入和输出遇到换行符时,标准I/O库执行操作
3. 不带缓冲,不对字符进行缓冲存储
标准错误流stderr通常是不带缓冲的
更改缓冲类型,需在流打开,但是为执行任何操作前调用:
void setbuf (FILE * __stream, char * __buf);
buf = NULL,关闭缓冲区
buf 长度为BUFSIZE,全缓冲
int setvbuf (FILE * __stream, char * __buf,int __modes, size_t __n);
buf、n : 全缓冲或行缓冲时有效,buf=NULL且带缓冲则缓冲区长度未BUFSIZE
modes :
_IOFBF 全缓冲
_IOLBF 行缓冲
_IONBF 不带缓冲
强制冲洗一个流,将缓冲区数据写入文件:
int fflush(FILE*fp);
fp: 文件流,为NULL时将导致所有输出流被冲洗。
打开流:
FILE *fopen (const char *__filename,const char * __modes);
filename : 文件名称
modes : 打开方式(b byte以二进制打开,含 + 读写,含 r 不创建,含 w 截断,含 a 追加)
r rb 只读
w wb 只写,将文件截断至0长,写创建
a ab 写追加,写创建
r+ r+b rb+ 读写
w+ w+b wb+ 读写,将文件截断至0长,创建
a+ a+b ab+ 文件尾端读写打开和创建
成功返回指针,失败返回NULL
FILE *freopen (const char *__filename,const char *__modes,FILE *__stream);
在指定的流上打开一个指定的文件,若流已经打开,则先关闭该流;若流已经定向,则使用freopen清除该定向;一般用于标准输出、标准输入和标准错误流重定向
FILE *fdopen (int __fd, const char *__modes);
读取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合
关闭流:
fclose(FILE *fp);
当程序正常终止时,所有缓冲的标准I/O流都被冲洗,所有打开的标准I/O流都被关闭。
读和写流:
一次读一个字符:
int getc(FILE *fp); // 这是个宏
int fgetc(FILE *fp); // 这是个函数
int getchar(); // 等价于 getc(stdin)
以上三个函数返回的是字符的ASCII码值,若获取出错或失败,则需要通过以下两个函数:
int ferror(FILE *fp);
int feof(FILE *fp);
FILE对象中维护了两个标志:
文件出错标志
文件结束标志
调用clearerr可以清除这两个标志
从流中读取数据后,可以调用ungetc将字符压送回流:
int ungetc(FILE *fp);
不能回送EOF,一次成功的ungetc会清除文件的EOF标志
一次写一个字符:
int putc(int c,FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c); // 等级于 putc(c,stdout);
每次读一行:
char *fgets(char *buf,int n,FILE *fp); // 最多返回n-1个字符,返回buf以null结尾
char *gets(char *buf); // 不安全,避免使用,返回值不含null
成功都是返回buf指针,否则返回NULL
每次输出一行:
int fputs(const char *str,FILE *fp);
int puts(const char *str);
标准I/O的效率:
二进制I/O:
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);
ptr: 读取或写入数据的指针
size: 对象内存的大小
nobj: 对象个数
fp: 文件描述符
返回值:对象个数
定位流:
long ftell(FILE *fp); // 成功返回文件offset,失败 -1
int fseek(FILE *fp,long offset,int whence); // 设置文件offset,whence : SEEK_SET\SEEK_CUR\SEEK_END;成功返回0,失败返回-1
void rewind(FILE *fp); // 将流设置到文件的起始位置
off_t ftello(FILE *fp); // 成功返回文件offset,失败 -1;可用于大文件,off_t = long int
int fseeko(FILE *fp,off_t offset,int whence); // 成功返回0,出错返回-1
int fgetpos(FILE *fp,fpos_t pos); // 超大文件,获取指定文件的offset
int fsetpos(FILE *fp,const fpos_t *pos); // 设置指定文件的offset
格式化I/O:
int prinf(const char * format,...); // 将格式化数据写到标准输出
int fprintf(FILE *fp,const char * format,...); // 将格式化数据写到指定流
int dprintf(int fd,const char * format,...); // 将格式化数据写到指定文件描述符
int sprintf(char * buf,const char * format,...); // 将格式化数据写到指定字符数组缓冲区
int snprintf(char * buf,size_t n,const char* format,...); // 将格式化数据写到指定字符数组缓冲区,显式设置字符数组缓冲区长度
以下五种将可变列表类型改为va_list:
int vprinf(const char * format,va_list arg); // 将格式化数据写到标准输出
int vfprintf(FILE *fp,const char * format,va_list arg); // 将格式化数据写到指定流
int vdprintf(int fd,const char * format,va_list arg); // 将格式化数据写到指定文件描述符
int vsprintf(char * buf,const char * format,va_list arg); // 将格式化数据写到指定字符数组缓冲区
int vsnprintf(char * buf,size_t n,const char* format,va_list arg); // 将格式化数据写到指定字符数组缓冲区,显式设置字符数组缓冲区长度
格式化输入:
int scanf(const char *format,...); // 从标准输入格式化输入
int fscanf(FILE *fp,const char *format,...); // 从指定流格式化输入
int sscanf(const char *buf,const char *format,...); // 从指定字符数组缓冲区格式化输入
可变长参数列表:
int vscanf(const char *format,va_list arg); // 从标准输入格式化输入
int vfscanf(FILE *fp,const char *format,va_list arg); // 从指定流格式化输入
int vsscanf(const char *buf,const char *format,va_list arg); // 从指定字符数组缓冲区格式化输入
实现细节:
获取流的文件描述符:
int fileno(FILE *fp);
临时文件:
char *tmpnam(char *ptr); // 指向唯一路径名的指针
FILE *tmpfile(void); // 成功返回文件指针,失败返回NULL
char *mkdtemp(char *template); // 创建有唯一名字的目录
int mkstemp(char *template); // 创建有唯一名字的文件
内存流:
像操作文件一样操作内存
FILE *fmemopen(void * buf,size_t size,const char *type); //打开内存,buf指向缓冲区地址,size指缓冲区的大小,type与fopen的modes参数一致
FILE *open_memstream(char **bufp,size_t *sizep); // 面向字节,只能写打开
FILE *open_wmemstream(wchar_t **bufp,size_t *sizep); // 面向宽字节,只能写打开
*/
#include <stdio.h>
#include <wchar.h>
void main()
{
// 重定向
// fwide(stdout, -1);
// setbuf(stdout,)
// getc(stdin);
int c = getchar(); // 获取输入值
putchar(c); // 输出值
ungetc('a', stdin); // 回退
int a = getc(stdin); // 读取
putc(a, stdout); // 写入
ungetc('b', stdin); //
int b = fgetc(stdin);
fputc(b, stdout);
off_t
}