LINUX,C标准IO要点解析
序
-
流注意区分文件IO和标准IO, 文件IO围绕文件描述符, 而标准IO围绕流进行操作,使用标准IO创建或者打开一个文件时,都已使一个流与之相关连(注意理解为流是自动与该文件关联的,我们操作是对流进行打开读写等操作)。
-
FILE对象: 在打开(fopen)一个流时与文件IO返回的文件描述符不同, 标准IO的fopen返回一个指向FILE对象的指针。 其中该FILE对象中包括该IO文件的文件描述符(所以标准IO时基于文件IO而封装的)、指向该流缓冲区的指针、缓冲区的长度(大小)、缓冲区内当前字符数及出错标志等。对流操作时,通常直接将FILE指针当作参数进行传递。
-
缓冲: 与文件IO无缓冲区的情况不同,对于标准IO来说,有 全缓冲、行缓冲和无缓冲三种形式:
1) 全缓冲:全缓冲的缓冲区需要用户编写程序时自行定义,只有当缓冲区满时才进行实际的IO操作(就例如往文件写入时,数据会先写入你定义的缓冲区内,当缓冲区满时才实际调用fwrite等进行IO操作,将数据写入文件内),包括一下的行缓存,这样的好处是可以大大减少read和write的调用次数,提高性能。
2) 行缓冲:行缓冲的形式就入往终端写命令时,需要回车(换行符)时才执行IO操作,标准IO库中已经定义了
行缓存·用来存放每一行数据的缓冲区的大小,无需用户定义。其中行缓存在遇到换行符、缓冲区满 或者需要从该流中获取输入的数据时,会冲洗(flush)所有行缓冲流。3)无缓冲: 字面意思,类似于使用fputs进行字符输出。
具体模型如下,标准IO不是直接往内核写数据,以上三种形式位有标准库提供的缓冲区的特点,所有有时执行行缓存或者全缓冲的函数时得不到输出,只是因为数据还在库缓冲区中。
提供求库缓冲区大小的思路如下,buf大小为10,通过改变次数t来确定缓冲区大小,只有t>=103时才有输出,最终得到大小位1024字节即1K(测试环境为linux 32位系统, windows测不出来,多少都会输出,也可能是vscode的优化(哭))
1.标准输入、标准输出、标准错误(stdin、stdout、stderr)
可理解为标准的输入、输出、错误流默认打开可自动的供进程使用,在标准IO中使用stdin、stdout、stderr三个预定义的文件指针来操作,在<stdio.h>中定义。
2.fopen
需要先声明FILE类型指针,
FILE * pfile;
FILE * fopen(const char * pathname, const char * type);
参数1:包含文件名的文件路径
参数2:
r : 只读方式打开这个文件,文件位置定位在开头, 若文件不存在,会报错
r+ : 可读可写的方式打开这个文件,文件位置(光标)定位在开头,若文件不存在,会报错
w : 只写的方式
如果文件不存在就创建这个文件,文件位置(光标)定位在开头
如果文件存在就清空(大小为0)这个文件,文件位置(光标)定位在开头
w+ : 可读可写的方式
如果文件不存在就创建这个文件,文件位置(光标)定位在开头
如果文件存在就清空(大小为0)这个文件,文件位置(光标)定位在开头
a : 只写的方式
如果文件不存在就创建这个文件,文件位置(光标)定位开头
如果文件存在 不清空这个文件,文件位置定位在在文件末尾
a+ : 可读可写的方式
如果文件不存在就创建这个文件,文件位置(光标)定位在开头
如果文件存在 不清空(大小为0)这个文件,文件位置定位在文件末尾
ab+ wb+ rb+
注意: 当给定“b”参数时,表示以二进制方式打开文件 binary
3.读和写 流
读和写 流操作的前提是已经使用fopen打开一个流。
1.输入函数
#include <stdio.h>
int getc(FILE *fp); //从流中读取一个字符
int fgetc(FILE *fp); //
int getchar(void); //getchar等同于 getc(stdin),经常用该函数与scanf搭配吃掉末尾得换行符
//三个函数 成功则返回下一个字符, 出错或者已达到文件末尾返回EOF
对于以上三个参数出错或者到达文件末尾都返回EOF,要区分到底是哪种错误情况,可以使用下面两个函数判断(条件为真返回非0, 否则返回0):
int ferror(FILE *fp);
int feof(FILE *fp);
void clearerr(FILE *fp); //清除对应流的以上两个标志(出错标志和文件结束标志)
2.输出函数
#include <stdio.h>
int putc(int c, FILE * fp);
int putc(int c, FILE * fp); //
int putchar(int c);
//以上三个函数,若成功返回字符c, 出错返回EOF;
3.行输入函数
#include <stdio.h>
char * fgets(char * restrict buf, int n, FILE * restrict fp); //从对应流中读,buf为自定义缓冲区,必须指定缓冲区的长度n
char * gets(char * buf); //从标准输入读(一般不推荐使用)
其中restrict为C语言中的一种类型限定符,用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。
注意fgets 使用的几种情况:
1)fgets读到换行符(‘\n’),结束读取, ‘\n’也被读走
2)fgets 读取size , 如果一直没有遇到"\n",就会读size-1 个字符, 最后末尾处需要有一个位置存放‘\0’ (null字节)
4.行输出函数
#include <stdio.h>
int fputs(const char * restrict str, FILE * restrict fp);
int puts(const char * str);
其中fputs将一个以null字节终止的字符串写到指定流是,末尾的终止符null并不写出。(随所以如果bull前没有换行符的话,并不意味着每次输出一行,很多时候需要自己根据实际情况来处理换行符)
5.二进制IO(fread、fwrite)
对于fgetc或者fputc,在读取数据时往往受到换行符和null(’\0’)字符的限制,无法完成对一个结构的一次性读取(可以读取但很麻烦且费时),所以fread、fwrite以二进制IO的形式进行读取,已实现对完整结构的读取。
#include <stdio.h>
size_t fread(void *restrict ptr, size_T size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
(明天继续写…)