cpp整理笔记:标准I/O的工作原理

  第一步:一般使用标准I/O的第一步是使用fopen()函数打开一个文件(stdin键盘文件,stdout、stderr屏幕文件这三个是自动打开的)。fopen()打开一个文件的同时,会建立一个缓冲区(读写模式下将建立两个缓冲区),还会创建一个包含文件和缓冲区相关数据的数据结构,最后fopen()返回一个指向该结构的指针fp,让其他函数能够找到这个结构。fopen()打开了一个流,如果文件以文本模式打开,就得到一个文本流,如果以二进制模式打开,就得到一个二进制流。

上述数据结构包含一个文件位置指示器,来确定在流中的当前位置,此外还包括错误指示器和文件结尾指示器、一个指向缓冲区起始处的指针、文件标识符和一个记录实际复制到缓冲区中的字符数计数器.

fopen()函数的数据结构 文件位置指示器 确定在流中的当前位置
错误指示器
文件结尾指示器
指向缓冲区起始处指针
文件标识符
记录实际复制到缓冲区中的字符数计数器

  第二步:调用stdio.h头文件中声明的某个输入函数,比如fgets()、gets()、fscanf()。调用这些函数能够将文件中的一块数据复制到缓冲区。初次函数调用还将设置fp坐直结构中的值。特别的,将设置流的当前位置和复制到缓冲区中的字节数,通常字节从0处开始。

  数据结构和缓冲区初始化后,输入函数将从缓冲区读取所请求的数据,文件位置指示器将被设置为紧跟其后的第一个字符的位置。因为stdio.h中所有输入函数使用一个缓冲区,所有每次调用函数后下一次调用函数时,在缓冲区里面都是紧跟在上一次后面,即后面的第一个字符开始。

  当输入函数读取完缓冲区的每一个字符时,它将会请求系统将一个缓冲区大小的数据存到缓冲区来继续读取,按此输入函数一直读到文件结尾,读取完最后最后一个缓冲区的最后一个字符时,文件结尾指示器的值将被设置为真,此时下一次输入函数有开始读取时,没有数据,文件结尾指示器将返回EOF。

  输出函数将数据写入缓冲区,过程和输入函数类似。


1.int ungetc(int c,FILE *fp)函数

int ungetc(int c,FILE *fo)函数将c指定的字符放回输入流中。如果向输出流中放入一个字符,下一次调用标准输入函数的时候就会读取那个字符。现在有一个例子,假如我们要通过一个函数来读取一个冒号之前的全部字符(不包括冒号本身),我们先用getchar()或者getc()一个个读取字符,一直到将冒号读取,然后使用ungetc()函数将冒号放回输入流中去。ANSI C 中规定每次只能放回一个字符,所以C允许将一行中多个字符放回输入流,那么现在输入函数就会和放回时相反的顺序来读取。

初始 a b c d e   f g
ch=getchar()   b c d e   f g
ungetc(ch,stdin) a b c d e   f g

2.int ffulsh()函数

原型:

int ffulsh(*fp)

调用fflush()函数可以将缓冲区的中任何未写的数据发送到一个有fp指向的输出文件。这个过程叫做刷新缓冲区(flushing 啊buffer)。假如fp是一个空指针的话,那么刷新掉所有的输出缓冲。对于一个输入流使用fflush()的效果没有定义。只要最近一次使用流的操作不是输入,就可以更新一个流。


3.int setvbuf()函数

原型:

int setvbuf(FILE *restrict fp,char *restrict buf,int mode,size_t size)
setvbuf()函数为标准I/O建立了一个替换缓冲区。打开文件之后,在没有对流进行任何操作之前,可以调用这个函数。指针fp来指定流,buf来指定使用的存储区。buf的值不是NULL话这个存储区就必须被创建。buf的值假如是NULL的话,函数就是自动给自己分配一个缓冲区。size变量为setvbuf()函数指定的数组的大小(size_t是一个派生类型),mode将从下列选项之中选取:_INFBF(完全缓冲,即缓冲区满的时候刷新)、_IOLBF表示行缓冲(缓冲区满的时候或者一个新行写入的时候刷新),还有_IONBF表示没有缓冲。函数执行成功返回零,否则非零。

 假定有一个存储数据对象(每个对象大小为3000字节)的程序,就可以使用setvbuf()函数创建一个缓冲区,大小视具体情况而定。



4.二进制I/O:fread()和fwrite()函数

举个例子:

double num=1./3.;
fprintf("fp,"%f",num);

这个%f可以要求为%.2f或者%.12f,读取文件没有办法恢复其完整的精度。总之,fprintf()函数以一种可能改变数字值的方式将其转化为字符串。

最精确和一致的数字存储方式就是使用与程序所使用的相同位格式,比如一个double类型的值就应该存放在一个double大小的单元里面。如果把数据存储在一个使用与程序相同表示方法的文件中,就称数据以二进制形式存储,这中间没有数字形式到字符串形式的转化。实际上,所有数据都是以二进制的方式进行存储的,甚至连字符也都是使用二进制表示来存储的额。然而,如果文件中的全部数据都以字符编码的形式被解读,那么该文件包含文本数据;如果这些数据的部分或者全部以二进制的数字数据被解读,就称文件包含二进制数据,机器语言指令数据也是二进制文件。


5.size_t fwrite()函数

原型:

size_t fwrite(const  void * restrict ptr,size_t size,size_t nmemb,FILE * restrict fp);

fwrite()将二进制数据写进文件。size_t是sizeof运算符返回的类型(通常是unsigned int 类型)。指针ptr是要写入的数据块的地址,size表示要写入的数据块的大小(单位为字节),nmemb表示数据块数目,fp指定要写入的文件。比如要保存一个256字节的数据对象(以数组为例):

char buffer[256];
fwrite(buffer,256,1,fp);

这样将一块256字节的数据块从缓冲区写入到文件;

也可以这样:

double earnings[10];
fwrite(earnings,sizeof(double),10,fp);

这样将earning里面的数据分成10块,每块都是double大小,写入文件。

注意:关于const void * restrict ptr,fwrite()的第一个参数不是固定的类型,比如可以使用字符型的buffer数组指针,可以使用double指针类型的earnings。

fwrite()函数返回成功写入的项目数。正常情况下,这个返回数应该和nmemb大小相等,不过要是写入发生错误的时候,可能这个返回数会小于nmemb的值。


6.size_t fread()函数

原型:

size_t fread(const void *restrict ptr,size_t size,size_t nmemb,FILE * restrict fp);
prt为读取文件数据的内存存储地址,fp为指定要读取的文件,要恢复之前保存的包含10个double数的数组,这个时候可以使用下面代码:
double earnings[10];
fread(earnings,sizeof(double),10,fp);



7.int feof(FILE *fp)函数和int ferror(FILE * fp)函数

当标准输入函数返回EOF的时候,通常表示已经到达了文件结尾。1.最近一次输入调用检测到文件结尾,feof()函数返回一个非零值,否则返回零值;2.读写错误,ferror函数返回一个非零值,否则返回零值。











你可能感兴趣的:(C)