一、标准IO简介
二、标准IO函数
(一)打开文件——fopen
打开文件操作fopen函数原型:
FILE *fopen(const char *pathname, const char *mode);
return : 失败时,返回NULL并设置errno。
成功时,返回一个FILE指针。
pathname : 需要打开的文件的路径。
mode : 打开文件的权限。
mode的值 | 相应的权限 |
"r" | 以只读形式打开文件,如果文件不存在时,会报错。 |
"w" | 以只写形式打开文件,如果文件存在,则会截断文件;如果文件不存在则会创建文件。(有则清空,无则创建) |
"a" | 以追加的形式打开文件,当在写文件时,文件指针会在文件最末尾处。 |
"r+" | 以可读可写形式打开文件,如果文件不存在时,会报错。 |
"w+" | 以可读可写形式打开文件,如果文件存在,则会截断文件;如果文件不存在则会创建文件。(有则清空,无则创建) |
"a+" | 可读可写,以追加的形式打开文件,当在写文件时,文件指针会在文件最末尾处。如果文件不存在,则会创建文件。 |
(二)关闭文件——fclose
关闭文件操作fclose函数原型:
int fclose(FILE *stream);
return : 失败返回EOF,并设置errno。
成功时,返回0;
stream : 需要关闭的文件流,fopen的返回值。
(三)读取/写入一个字符——fgetc/fputc
fgetc函数原型:
int fgetc(FILE *stream);
return : 失败时,返回EOF。
成功时,将获取到的字符转换为unsigned int类型。
stream : 从stream流中读取。
fputc函数原型:
int fputc(int c, FILE *stream);
return : 失败时,返回EOF。
成功时,将获取到的字符转换为unsigned int类型。
c : 需要写入的字符。
stream : 需要写入的文件流stream。
(四)读取/写入一个字符串——fgets/fputs
读取一个字符串fgets函数原型:
char *fgets(char *s, int size, FILE *stream);
return : 失败时,返回NULL。
成功时,返回读取到的字符串的指针,既为s。
s : 将读取到的字符串保存在s所指向的内存中 。
size : 需要读取的大小,以字节为单位,最终读取到的内容的大小为(size-1)个。
stream : 需要读取的文件的文件流stream。
向文件中写入一个字符串fputs的函数原型:
int fputs(const char *s, FILE *stream);
return : 失败时,返回EOF。
成功时,返回实际向文件中写入的数据长度,以字节为单位。
s : 需要向文件中写入的内容。
stream : 需要写入的文件的文件流。
(五)文件指针的操作
1、文件指针位置的调整——fseek/fseeko
fseek/fseeko函数原型:
int fseek(FILE *stream, long offset, int whence);
int fseeko(FILE *stream, off_t offset, int whence);
int :失败时,返回EOF。
成功时,返回0;
stream :需要文件指针的文件的文件流。
offset :相对于参考位置的偏移量。
whence : 参考位置。上一篇系统调用章节有解释。
更推荐使用fseeko函数。
2、获取文件指针的当前位置——ftell/fello
ftell/fello的函数原型:
long ftell(FILE *stream);
off_t ftello(FILE *stream);
return : 失败时,返回EOF。
成功时,文件的当前相对于SEEK_SET的偏移量;
stream : 需要获取文件指针的文件流。
更推荐使用ftello函数。
3、将文件指针设置于文件开始——rewind
rewind函数的函数原型:
void rewind(FILE *stream);
#此函数等价于(void) fseek(stream, 0L, SEEK_SET)
三、系统调用和标准IO的对比
(一)联系
标准IO在在实现的内里是调用的系统IO,在可移植方面有更好的支持。因此在标准IO和系统调用都可使用的时候优先使用标准IO。
(二)区别
在讲两者的区别前有必要讲一下输入/输出缓冲区的概念。
当我们在使用fputc,fprinf等输出函数时,并不会直接输入到文件中,而是将需要输出的内容先保存到输出缓冲区中,然后fputc,fprinf等输出函数从输出缓冲区中获取数据。下面是一个示例:
#include <stdio.h>
int main (){
fprintf(stdout,"Hello World");
while(1);
return 0;
}
程序编译运行后并没有输出Hello World,而是进入死循环卡死。
缓存类型:
全缓存 | 在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是 对磁盘文件的读写 。 |
行缓存 | 在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等 按下回车键换行 时才进行实际的I/O操作。典型代表是 标准输入(stdin) 和 标准输出(stdout) 。 |
不缓存 | 也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。 |
此时如果需要立即输入/输出可以手动刷新输入/输出缓冲区。使用:
int fflush(FILE *stream);
//stream为NULL时,刷新所有的文件缓冲区
1、系统调用
使用系统调用时,会从用户态转为内核态,同时会刷新输入/输出缓冲区。带来的好处就是输入/输出的响应速度快。
2、标准IO
只有在全缓存缓存满、行缓存遇到换行符时,或者手动刷新缓冲区时进行写入。这带来的好吃就是吞吐量大。