C标准中的I/O库是一个比较庞大的库,实现也比较复杂。显然I/O库的实现是依赖于操作系统的,不同的系统上I/O库的实现机理是不一样的。glibc中,I/O库的核心实现在libio目录下。有4个头文件libio.h, iolibio.h, libioP.h, strfile.h, stdio.h,其中stdio.h是导出的标准C头文件,其余都是内部实现。这里并不打算解剖整个I/O库的源代码(那样可能一本书也说不完),只是概述性的介绍一下它的轮廓和所有标准接口的功能。我们学习I/O库也是为了能在实践中应用它,因此因此对C标准中所有的I/O接口要有个全面的了解。
输入/输出函数主要在stdio.h中,对应的宽字符版本在wchar.h中,下面列出标准C中定义的所有接口:
1、相关类型和常量:FILE、EOF在stdio.h中;wchar_t、wint_t、WEOF、WCHAR_MAX、WCHAR _MIN等在wchar.h中定义。size_t和NULL在stdio.h, wchar.h和stddef.h均会定义,同时使用其中的多个头文件是无害的。
2、fopen,fclose,fflush,frepoen,fwide,FOPEN_MAX,FILENAME_MAX:打开文件并返回数据流、关闭文件、清空数据流的缓冲区、将文件重新关联到一个数据流、fwide在wchar.h中用于设置与测试流定向、可以同时打开的数据流最大个数、文件名的最大长度。
文件访问方式:”r”、”w”、”a”、”r+”、”w+”、”a+”,所有访问方式后面都可以加上字母b,表示数据流保存二进制数据,而不是字符数据。
3、setvbuf,setbuf,BUFSIZ,_IOFBF,_IOLBF,_IONBF:设置数据流的缓冲区、BUFSIZ缓冲区的默认长度、后面三个是缓冲模式,为数完全缓冲、行缓冲(写入换行符或缓冲区满时刷新缓冲区)、数据流不缓冲。
4、stdin,stdout,stderr:标准输入流、输出流、错误流。除stderr之外,另外两个数据流都缓冲。C语言程序执行时,会预先打开这3种文本流。
5、fseek,ftell,rewind,fgetops,fsetpos:设置流的读写位置、返回流的当前读写位置、将流的位置恢复到开头、fgetpos/fsetpos用于获取或设置流的位置,其值放在fpos_t类型的参数中。定位码有SEEK_SET, SEEK_CUR,SEEK_END。
6、fgetc,fgetwc,getc,getwc,getchar,getwchar,ungetc,ungetwc:从流中读取下一个字符并作为int类型的值返回、getchar则从标准输入流中读取、ungetc系列将字符c推回到输入流中。
7、fgets,fgetws,gets:从流中读取一个长为n的字符串到s中,直到遇到换行符、文件末尾或读取了n-1个字符,然后在字符串末尾加上终止符、gets则从标准输入流中读取。
8、fscanf,fwscanf,scanf,wscanf,sscanf,swscanf:格式化输入函数。从流中读取字符并根据控制字符串把字符转换成相应的值。控制字符串后面的每个参数应为指针,从输入流读取的值转换之后存放在指针指向的对象中。
控制字符串:除空白符和%号以外的任何其他字符都要匹配输入流中下一个字符。如果不匹配,则发生冲突,终止转换操作。指针参数要有正确的个数和正确的类型,符合控制字符串中的转换说明。
scanf系列的转换说明以%号开始,然后依次出现下列元素:
(1)可选的赋值取消标志符:*
(2)可选的最大字段宽度:表示为正的十进制整数
(3)可选的长度说明符:有hh,h,l,ll,j,z,t或L
(4)必要的转换说明符:有a,c,d,e,f,g,i,n,o,p,s,u,x,%或[...]。
常用的转换说明:%d,%i,%u,%o,%x,%c,%s,%f,%e,%%。
9、fputc,fputwc,putc,putwc,putchar,putwchar:向输出流中写入一个字符、putchar则向标准输出流写入。
10、fputs,fputws,puts:向输出流中写入一个字符串,不包括null终止符、puts则向标准输出流写入。
11、fprintf,printf,sprintf,snprintf,fwprintf,wprintf,swprintf:格式化输出函数。根据控制字符串表示的格式,把输出转换成一系列格式发送到输出流中。
printf系列的转换说明以%号开始,然后依次出现下列元素:
(1)0个或多个标志字符:-,+,0,#或空格,修改转换操作的含义
(2)可选的最小字段宽度:表示为十进制整数常量
(3)可选的精度说明:表示为小数点加一个十进制整数
(4)可选的长度说明符:有ll,l,L,h,hh,j,z或t
(5)转换操作:a,A,c,d,e,E,f,g,G,i,n,o,p,s,u,x,X或%。
常用的转换操作:%d,%12d,%u,%o,%c,%s,%p,%f,%e,%%。
12、vfprintf,vprintf,vsprintf,vfscanf,vscanf,vsscanf,vfwprintf,vwprintf,vswprintf,vfwscanf,vwscanf,vswscanf:与上面的printf和scanf系列类似,只不过不是用省略号作为可变参数,而用stdarg.h中的va_list类型作为一个参数,这就允许编程人员定义自己的格式化输入输出函数。
13、fread,fwrite:从流中读取count个指定长度的元素到数组中、把数组中的count个指定的长度的元素写入到流中,返回实际读取或写入的项数。
14、feof,ferror,clearerr:判断流是否到达末尾(到达返回非0,没到达返回0)、返回流的错误状态(有错误返回非0,没错误返回0)、清除流中的任何文件结束标志或错误标志。
15、remove,rename:删除指定文件、重命名指定文件。
16、tmpfile,tmpnam,L_tmpnam,TMP_MAX:tmpfile生成一个临时文件并以可读方式打开,文件关闭或程序终止时文件会被删除、tmpnam生成一个不与当前各个文件名冲突的新文件名,保存在参数buf中、L-tmpnam为buf的最小字符个数、TMP_MAX为连续调用tmpnam的最大次数,不小于25。
17、perror:打印保存在errno全局变量中的当前错误码的消息描述。
每个标准I/O函数的代码并不长,因为glibc在内部实现了一个基本的I/O库,标准I/O函数都是调用这些内部函数来完成工作的。所有的内部函数接口都在iolibio.h中声明,基本上每个标准I/O函数都对应一个内部函数。内部函数名在标准函数名前加了__IO_字样,数据流类型也使用内部__IO_FILE类型,标准的FILE类型就是直接用__IO_FILE类型定义的。libio.h中则声明了一些内部实现需要使用的类型和常量,strfile.h声明了与缓冲区、缓冲字符有关的一些特性。