《c和指针》之总结 七
15、输入/输出函数
标准规定了标准函数库中的函数的接口和操作,这有助于提高程序的可移植性。一种编译器可以在它的函数库中提供额外的函数,但不应修改要求提供的函数。
perror函数提供了一种向用户报告错误的简单方法。当检测到一个致命的错误时,你可以使用exit 函数终止程序。
void perror(char const *message);如果message不是NULL 并且指向一个非空的字符串,perror 函数就打印出这个字符串,后面跟一个分号和一个空格,然后打印一条用于解释errno 当前的错误代码的信息。
void exit(int status);status 参数返回给操作系统,用于提示程序是否正常完成。这个值和main函数返回的整型状态值相同。预定义符号EXIT_SUCCESS 和EXIT_FAILUREE 分别提示程序的终止是成功还是失败。
stdio.h 头文件包含了使用I/O 库函数所需要的声明。所有的I/O 操作都是一种在程序中移植或移出字节的事务。函数库为I/O所提供的接口称为流。在缺省的情况下,流I/O 是进行缓冲的。二进制流主要用于二进制数据,字节不经修改的从二进制流读取或向二进制流写入。另一方面,文本流则用于字符。文本流能够允许的最大文本行因编译器而异,但至少允许254个字符。根据定义,行由一个换行符结尾。如果宿主操作系统使用不同的约定结束文本行,I/O函数必须在这种形式和文本行的内部形式之间进行翻译转换。
FILE是一种数据结构,用于管理缓冲区和存储流的I/O状态。运行时环境为每个程序提供了三个流-----标准输入、标准输出和标准错误。最常见的情况是把标准输入缺省设置为键盘,其他两个流缺省设置为显示器。错误信息使用一个单独的流,这样即使标准输出的缺省值重定向为其他位置,错误信息仍能够显示在它的缺省位置。FOPEN_MAX 是你能够同时打开的最多文件数,具体数目因编译器而异,但不能小于8。FILENAME_MAX 是用于存储文件名的字符数组的最大限制长度。如果不存在长度限制,这个值就是推荐最大长度。
为了对一个文件执行流I/O 操作,首先必须用fopen 函数打开文件,它返回一个指向FILE 结构的指针,这个FILE 结构指派给进行操作的流。这个指针必须在一个FILE * 类型的变量中保存。然后,这个文件就可以进行读取和(或)写入。读写完毕后,应该关闭文件。许多I/O 函数属于同一个家族,他们在本质上执行相同的任务,但在从何处读取或何处写入方面存在一些微小的差别。通常一个函数家族的各个变型包括接受一个流参数的函数,一个只用于标准流之一的函数以及一个使用内存中的缓冲区而不是流的函数。
流用fopen 函数打开。它的参数是需要打开的文件名和需要采用的流模式。模式指定流用于读取、写入还是添加,它勇士指定流为二进制流还是文本流。freopen 函数用于执行相同的任务,但你可以自己指定需要使用的流。这个函数最常用于重新打开一个标准流。你应该始终检查fopen 或freopen函数的返回值,看看有没有发生错误。在结束了一个流的操作之后,你应该使用fclose 函数将它关闭。
FILE * fopen( char const *name, char const *mode);下标为常用的模式:
|
|
读取 |
写入 |
添加 |
|
文本 |
“r” |
“w” |
“a” |
|
二进制 |
“rb” |
“wb” |
“ab” |
FILE *freopen(char const *filename, char const *mode, FILE *stream);最后一个参数就是需要打开的流。它可能是一个先前从fopen 函数返回的流,也可能是标准流stdin、stdout、stderr。这个函数首先试图关闭这个流,然后用指定的文件和模式重新打开这个流。如果打开失败,函数返回一个NULL 值。如果打开成功,函数就返回它的第3个参数值。
int fclose(FILE *f);对于输出流,fclose 函数在文件关闭之前刷新缓冲区。如果它执行成功,fclose 返回零值,否则返回EOF。
逐字符的I/O 由getchar和putchar 函数家族实现。输入函数fgetc 和getc 都接受一个流参数,getchar 则只从标准输入读取。第1个以函数的方式实现,后两个则以宏的方式实现。他们都返回一个用整型值表示的单字符。除了用于执行输出而不是输入之外,fputc、putc和putchar 函数具有和对应的输入函数相同的属性。ungetc用于把一个不需要的字符退回到流中。这个被退回的字符将是下一个输入操作返回的第1个字符。改变流的位置(定位)将导致这个退回的字符被丢弃。
gets 和puts函数家族是用于操作字符串而不是单个字符。
char *fgets(char *buffer,int buffer_size,FILE *stream);
char *gets(char *buffer);
int fputs(char const *buffer ,FILE *stream);
int puts(char const *buffer);
fgets从指定的stream读取字符并把他们复制到buffer中。当它读取一个换行符并存储到缓冲区之后就不再读取。如果缓冲区内存储的字符数达到buffer_size – 1 个时它也停止读取。在这种情况下,并不会出现数据丢失的情况,因为下一次调用fgets将从下一个字符开始读取。在任何一种情况下,一个NUL 字节将被添加到缓冲区所存储的数据的末尾,使他成为一个字符串。
如果在任何字符读取前就到达了文件尾,缓冲区就未进行修改,fgets 函数返回一个NULL 指针。否则,fgets 返回它的第1个参数(指向缓冲区的指针)。这个返回值通常用于检查是否到达了文件尾。
传递给fputs的缓冲区必须包含一个字符串,它的字符被写入到流中。这个字符串预期以NUL 字节结尾,所以这个函数没有一个缓冲区长度参数。这个字符串是逐个字写入的:如果它不包含一个换行符,就不会写入换行符。如果它包含了好几个换行符,所有的换行符都会被写入。因此,当fgets每次都读取一整行时,fputs却既可以一次写入一行的一部分,也可以一次写入一整行,甚至可以一次写入好几行。如果写入时出现了错误,fputs 返回常量值EOF,否则它将返回一个非负值。
gets和puts函数几乎和fgets与fputs相同。之所以存在他们是为了允许向后兼容。他们之间的一个主要的功能性区别在于当gets 读取一行输入时,它并不在缓冲区中存储结尾的换行符。当puts写入一个字符串时,它在字符串写入之后向输出再添加一个换行符。
行I/O既可以是格式化的,也可以是未格式化的。gets和puts 函数家族执行未格式化的行I/O。
scanf和printf 函数家族执行格式化的I/O操作。输入函数共三种,fscanf 接受一个流参数,scanf从标准输入读取,sscanf从一个内存中的缓冲区接收一个字符。printf 家族也有三个函数,他们的属性也类似。scanf 家族的函数根据一个格式字符串对字符进行转换。一个指针参数列表用于提示结构值的存储地点。函数的返回值是被转换的值的个数,如果没有任何值被转换就遇到文件尾,函数返回EOF。printf 家族的函数根据一个格式字符串把值转换为字符形式。这些值是作为参数传递给函数的。
使用二进制流写入二进制数据(如整数和浮点数)比使用字符I/O效率更高。二进制I/O直接读写值的各个位,而不必把值转换为字符。但是,二进制输出的结构非人眼所能阅读。fread和fwrite函数执行二进制I/O操作。每个函数都接受4个参数:指向缓冲区的指针、缓冲区中每个元素的长度、需要读取或写入的元素个数以及需要操作的流。
size_t fread(void *buffer , size_t size, size_t count, FILE *stream);
size_t fwrite(void *buffer , size_t size, size_t count, FILE *stream);
buffer是一个指向用于保存数据的内存位置的指针,size 是缓冲区中每个元素的字节数,count 是读取或写入的元素数,当然stream是数据读取或写入的流。buffer参数被解释为一个或多个值的数组。count参数指定数组中有多少个值,所以读取或写入一个标量时,count的值应为1。函数的返回值是实际读取或写入的元素(并非字节)数目。如果输入过程中遇到了文件尾或者输出过程中出现了错误,这个数字可能比请求的元素数目要小。
在缺省情况下,流是顺序读取的。但是,你可以通过在读取或写入之前定位到一个不同的位置实现随机I/O操作,fseek 函数允许你指定文件中的一个位置,它用一个偏移量表示,参考位置可以是文件起始位置,也可以使文件的当前的位置,还可以是文件的结尾位置。ftell函数返回文件的当前位置。fsetpos 和fgetpos 函数是前两个函数的替代方案。但是fsetpos函数的参数只有当它是先前从一个作用于同一个流的fgetpos函数的返回值时才是合法的。最后,rewind函数返回到文件的起始位置。
fflush函数 它迫使一个输出流的缓冲区内的数据进行物理写入,不管他是不是已经写满,如保证调试信息实际打印出来,而不是保存在缓冲区中直到以后才打印出来。原型为:int fflush(FILE *stream);
long ftell(FILE *stream);
int fseek(FILE *stream, long offset, int from);
void rewind(FILE *stream);
int fgetpos(FILE *stream, fpos_t *position);
int fsetpos(FILE *stream, fpos_t const *position);
rewind 函数将读/写指针设置回指定流的起始位置。它同时清除流的错误提示标志。fgetpos和fsetpos函数分别是ftell和fseek的函数的替代方案。他们的主要区别在于这对函数接受一个指向fpos_t 的指针作为参数。fgetpos 在这个位置存储文件的当前位置,fsetpos把文件位置设置为存储在这个位置的值。
在执行任何流操作之前,调用setbuf函数可以改变流所使用的缓冲区。用这种方式指定一个缓冲区可以防止系统为流动态分配一个缓冲区。向这个函数传递一个NULL 指针作为缓冲区参数表示禁止使用缓冲区。setvbuf函数更为通用。使用它,你可以指定一个并非标准长度的缓冲区。你也可以选择你所希望的缓冲方式:全缓冲、行缓冲或不缓冲。
void setbuf(FILE *stream, char *buf);
int setvbuf(FILE *stream, char *buf,int mode, size_t size);
setbuf 设置了另一个数组,用于对流进行缓冲。这个数组的字符长度必须为BUFSIZ(在stdio.h中定义) 。为一个流自行指定缓冲区可以防止I/O函数库为它动态分配一个缓冲区。如果一个NULL 参数调用这个函数,setbuf 函数将关闭流的所有缓冲方式。字符准确地程序所规引的方式进行读写。
setvbuf 函数更为通用。mode的参数用于指定缓冲的类型。_IOFBF指定一个完全缓冲的流,_IONBF 指定一个不缓冲的流,_IOLBF指定一个行缓冲流。所谓行缓冲,就是每当一个换行符写入到缓冲区时,缓冲区便进行刷新。
ferror和clearerr函数和流的错误状态有光,也就是说,是否出现了任何读/写错误。第1个函数返回错误状态,第2个函数重置错误状态。如果流当前位于文件尾,那么feof函数就返回真。这个状态可以通过对流自行fseek、rewind和fsetpos函数来清除。
int feof(FILE *stream);
int ferror(FILE *stream);
void clearerr(FILE *stream);
tmpfile 函数返回一个与一个临时文件关联的流。当流被关闭之后,这个文件被自动删除。tmpnam函数为临时文件创建一个合适的文件名。这个名字不会与现存的文件名冲突。把文件名作为参数传递给remove 函数可以删除这个文件。renam 函数用于修改一个文件的名字。它接受两个参数,文件的当前名字和文件的新名字。
FILE *tmpfile(void);这个函数创建一个文件,当文件被关闭或程序终止这个文件便自动删除。该文件一wb+ 模式打开,这使它可用于二进制和文本数据。
char *tmpnam(char *name);如果传递给函数的参数为NULL,那么这个函数便返回一个指向静态数组的指针,该数组包含了被创建的文件名。否则,参数便假定是一个指向长度至少为L_tmpnam 的字符数组的指针。在这种情况下,文件名在这个数组中创建,返回值就是这个参数。无论哪种情况,这个被创建的文件名保证不会与已经存在的文件名同名。只要调用次数不超过TMP_MAX次,tmpnam 函数每次调用时都能产生一个新的不同的名字。
int remove(char const *filename);
int rename(char const *oldname,char const *newname);
如果执行成功,这个两个函数都返回零值。如果失败,他们都返回非零值。

浙公网安备 33010602011771号