分类: LINUX
2008-11-19 16:07:16
标准I/O库及其头文件stdio.h为底层I/O系统调用提供了一个通用的接口。这个库现在已经成为ANSI标准C的一部分,而我们前面见到的系统调用却还不是。标准I/O库提供了许多复杂的函数,用于格式化输出和扫描输入。它还负责满足设备的缓冲需求。
在很多方面,使用标准I/O库和使用底层文件描述符类似。你需要先打开一个文件以建立一个访问路径。这个操作的返回值将作为其他I/O库函数的参数。在标准I/O库中,与底层文件描述符对应的对等物叫流(stream),它被实现为指向结构FILE的指针。
注意,不要把这里的文件流与C++语言中的输入输出流(iostream)以及AT&T UNIX System
VRelease 3中引入的进程间通信STREAMS模型相混淆,STEAMS模型方面的问题已超出本书的讨论范围。要想进一步了解STREAMS,请查阅X/Open规范()和随System V版本一起提供的AT&T STREAMS Programming Guide(《AT&T STREAMS 程序设计指南》)。
在启动程序时,有三个文件流是自动打开的。它们是stdin、stdout和stderr。它们都是在stdio.h头文件里定义的,分别代表着标准输入、标准输出和标准错误输出,与底层文件描述符0、1和2相对应。
在接下来的内容里,我们将学习标准I/O库中的下列库函数:
fopen、fclose
fread、fwrite
fflush
fseek
fgetc、getc、getchar
fputc、putc、putchar
fgets、gets
printf、fprintf和sprintf
scanf、fscanf和sscanf
*
fopen 函数
fopen库函数类似于底层的open系统调用。它主要用于文件和终端的输入输出。如果需要对设备的行为进行明确的控制,那最好使用底层系统调用,因为这可以避免用库函数带来的一些非预期的潜在副作用,如输入/输出缓冲
语法:
#include
FILE *fopen(const char *filename, const char *mode);
fopen打开由filename参数指定的文件,并把它与一个文件流关联起来。mode参数指定文件的打开方式,它是下列字符串中的一个:
“r”或“rb”:以只读方式打开。
“w”或“wb”:以写方式打开,并把文件长度截短为零。
“a”或“ab”:以写方式打开,新内容追加在文件尾。
“r+”或“rb+”或“r+b”:以修改方式打开(读和写)。
“w+”或“wb+”或“w+b”:以修改方式打开,并把文件长度截短为零。
“a+”或“ab+”或“a+b”:以修改方式打开,新内容追加在文件尾。
字母b表示文件是一个二进制文件而不是文本文件
请注意,UNIX和Linux并不像MS-DOS那样区分文本文件和二进制文件。它们对所有文件都一视同仁,把文件都看作是二进制文件。另一个需要注意的地方是mode参数,它必须是字符串,而不是字符。所以总是应该使用"r",而不是'r'。
fopen在成功时返回一个非空的FILE *指针。失败时返回NULL值,NULL值的定义在头文件stdio.h里。
*
fread函数
fread库函数的作用是从一个文件流里读取数据。数据从文件流stream读到由ptr指定的数据缓冲区里。fread和fwrite都是对数据记录进行操作的,size参数指定每个数据记录的长度,计数器nitems给出要传输的记录个数。它的返回值是成功地读到数据缓冲区里的记录个数(而不是字节数)。当到达文件尾时,它的返回值可能会小于nitems,甚至可以是零。
该函数原型如下:
#include
size_t fread(void *ptr, size_t size, size_t nitems, FILE
*stream);
对所有向缓冲区里写数据的标准I/O函数来说,为数据分配空间和检查错误的工作是程序员的责任。参见本章后面对ferror和feof函数的介绍。
*
fwrite 函数
fwrite库调用与fread有相似的接口。它从指定的数据缓冲区里取出数据记录,并把它们写到输出流中。它的返回值是成功写入的记录个数。
该函数原型如下:
#include
size_t fwrite (const void *ptr, size_t size, size_t
nitems, FILE *stream);
请注意,我们不推荐把fread和fwrite用于结构化数据。部分原因在于用fwrite写的文件在不同的计算机之间可能不具备可移植性。
*
fclose 函数
fclose库函数关闭指定的文件流stream,使所有尚未写出的数据都写出。因为stdio库会对数据进行缓冲,所以使用fclose是很重要的。如果程序需要确保数据已经全部写出,就应该调用fclose函数。虽然当程序正常结束时,会自动对所有还打开的文件流调用fclose函数,但这样做就没有机会检查由fclose报告的错误了。与文件描述符一样,可用文件流的数目也是有限制的。这个限制由头文件stdio.h中的FOPEN_MAX常量定义,最小为8。
语法:
#include
int fclose(FILE *stream);
*
fflush 函数
fflush库函数的作用是把文件流里的所有未写出数据立刻写出。例如,你可以用这个函数来确保在试图读入一个用户响应之前,先向终端送出一个交互提示符。使用这个函数还可以确保在程序继续执行之前重要的数据都已经被写到磁盘上。有时在调试程序时,还可以用它来确定程序是正在写数据而不是被挂起了。注意,调用fclose函数隐含执行了一次flush操作,所以不必在fclose之前调用fflush。
语法:
#include
int fflush(FILE *stream);
*
fseek 函数
fseek函数是与lseek系统调用等价的文件流函数。它在文件流里为下一次读写操作指定位置。offset和whence参数的含义和取值与前面的lseek系统调用完全一样。但lseek返回的是一个off_t数值,而fseek返回的是一个整数:0表示成功,-1表示失败并设置errno指出错误。
函数原型如下:
#include
int fseek(FILE *stream, long int offset, int whence);
*
fgetc、getc 和getchar 函数
fgetc函数从文件流里取出下一个字节并把它作为一个字符返回。当它到达文件尾或出现错误时,它返回EOF。你必须通过ferror或feof来区分这两种情况。
函数原型如下:
#include
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar();
getc函数的作用和fgetc一样,但它有可能被实现为一个宏,如果是这种,stream参数就不能有副作用(例如,它既不能影响非局部变量也不能影响做为参数传递给函数的变量)。同时,也不能使用getc的地址作为一个函数指针。
getchar函数相当于getc(stdin),它从标准输入里读取下一个字符。
*
fputc、putc 和putchar 函数
fputc函数把一个字符写到一个输出文件流中。它返回写入的值,如果失败,则返回EOF。
#include
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
类似于fgetc和getc之间的关系,putc函数的作用也相当于fputc,但它可能被实现为一个宏。GUN C编译器就是这么做的,你可以在头文件stdio.h中看到它的定义。
putchar函数相当于putc(c ,stdout),它把单个字符写到标准输出。注意,putchar和getchar都是把字符当作int而不是char来使用的。这就允许文件尾(EOF)标识取值-1,这是一个超出字符数字编码范围的值。
*
fgets 和gets 函数
fgets函数从输入文件流stream里读取一个字符串。它把读到的字符写到s指向的字符串里,直到出现下面这几种情况之一:遇到换行符,已经传输了n-l个字符,或者到达文件尾。它会把遇到的换行符也传递到接收字符串里去,再加上一个表示结尾的空字节\ 0。一次调用最多只能传输n-1个字符,因为它必须把空字节加上以结束字符串。
#include
char *fgets(char *s, int n, FILE
*stream);
char *gets(char *s);
当它成功完成时,它返回一个指向字符串s的指针。如果文件流已经到达文件尾,fgets会设置这个文件流的EOF标识并返回一个空指针。如果出现读错误,fgets返回一个空指针并设置errno给出错误的类型。
gets函数类似于fgets,只不过它从标准输入读取数据并丢弃遇到的换行符。它在接收字符串的尾部加上一个null字节。
注意:gets对传输字符的个数并没有限制,所以它可能会溢出自己的传输缓冲区。因此应该避免使用它并用fgets来代替。因特网上的许多安全问题都可以追溯到在程序中使用了可能造成各种缓冲区溢出的函数,gets就是一个这样的函数,所以千万要小心!