perror函数以一种简单、统一的方式报告错误。其简化向用户报告这些特定错误的过程,其原型为:
void perror(char const *message);
如果message不是NULL并且指向一个非空的字符串,perror函数就打印出这个字符串,后面跟一个分号和一个空格,然后打印出一条用于解释errno当前错误代码的信息。
只有当一个库函数失败时,errno才会被设置。当函数成功运行时,errno的值不会被修改。这意味着我们不能通过测试errno的值来判断是否有错误发生。反之,只有当被调用的函数提示有错误发生时检查errno的值才有意义。
exit函数用于终止一个程序的执行,其原型为:
void exit(int status);
status参数返回给操作系统,用于提示程序是否正常完成。这个值和main函数返回的整型状态值相同。预定义符号EXIT_SUCCESS和EXIT_FAILURE分别提示程序的终止是成功还是失败。
当程序发现错误情况使它无法继续执行下去时,这个函数尤其有用。经常会在调用perror之后再调用exit终止程序。
流分为两种类型:文本流和二进制流。文本流的有些特性在不同的系统中可能不同,其中之一就是文本行的最大长度,另一个可能不同的特性是文本行的结束方式。二进制流中的字节将完全根据程序编写它们的形式写入到文件或设备中,而且完全根据它们从文件或设备读取的形式读入到程序中。这种类型的流适用于非文本数据,但如果你不希望I/O函数修改文本文件的行末字符,也可以把它用于文本文件。
对于每个ANSI C程序,运行时系统必须提供至少3个流——标准输入、标准输出和标准错误。这些流的名字分别为stdin,stdout和stderr,它们都是一个指向FILE结构的指针。
I/O函数以3种基本的形式处理数据:单个字符、文本行和二进制数据。对于每种形式,都有一组特定的函数对它们进行处理。
(1)字符:getchar,putchar,读取(写入)单个字符
(2)文本行:gets,puts,未格式化的文本行输入(输出);scanf,printf,格式化的文本行输入(输出)
(3)二进制数据:fread,fwrite,读取(写入)二进制数据
其中的%l(L)表示long,%ld表示输入的是long型整数,%lf表示输入的是double型浮点数。
%1(一)表示一个字节,即8个二进制位,%1d表示用一个字节表示的整数。
fopen函数打开一个特定的文件,并把一个流和这个文件相关联,其原型为:
FILE *fopen(char const *name, char const *mode);
mode 参数的可选值有:
(1)文本:读取’r’,写入’w’,添加’a’
(2)二进制:读取"rb",写入"wb",添加"ab"
如果一个文件打开是用于读取的,那么它必须是原先已经存在的。但如果一个文件打开是用于写入的,如果它原先已经存在,那么它原来的内容就会被删除。如果它原先不存在,那么就创建一个新文件。如果一个打开用于添加的文件原先不存在,那么它将被创建,如果原先已经存在,其原先内容并不会被删除。无论在哪一种情况下,数据只能从文件的尾部写入。
如果fopen函数执行成功,它返回一个指向FILE结构的指针,该结构代表这个新创建的流。如果函数执行失败,它就返回一个NULL指针。
freopen函数用于打开(或重新打开)一个特定的文件流,其原型为:
FILE *freopen(char const *filename, char const *mode, FILE *stream);
该函数首先试图关闭这个流,然后用指定的文件和模式重新打开这个流。如果打开失败,函数返回一个NULL值。如果打开成功,函数就返回它的第3个参数值。
fclose函数关闭流,其原型为:
int fclose(FILE *f);
对于输出流,fclose函数在文件关闭之前刷新缓冲区。如果它执行成功,fclose返回零值,否则返回EOF。
ungetc函数把一个先前读入的字符返回到流中,这样它可以在以后被重新读入,其原型为:
int ungetc(int character, FILE *stream);
行I/O可以用两种方式执行——未格式化的或格式化的。这两种形式都用于操纵字符串。区别在于未格式化的I/O简单读取或写入字符串,而格式化的I/O则执行数字和其他变量的内部和外部表示形式之间的转换。
未格式化的原型有:
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);
格式化的原型有:
int fscanf(FILE *stream, char const *format, ...);
int scanf(char const *format, ...);
int sscanf(char const *string, char const *format, ...);
int fprintf(FILE *stream, char const *format, ...);
int printf(char const *format, ...);
int sprintf(char *buffer, char const *format, ...);
在任何scanf系列函数的每个非数组、非指针参数前都要加上&符号。
使用printf结果输出送到标准输出;使用fprintf可以使用任何输出流,而sprintf把它的结果作为一个NUL结尾的字符串存储到指定的buffer缓冲区而不是写入到流中。
把数据写到文件效率最高的方法是用二进制形式写入。二进制输出避免了在数值转换为字符串过程中所涉及的开销和精度损失。但二进制数据并非人眼所能阅读,所以这个技巧只有当数据将被另一个程序按顺序读取时才能使用。
fread函数用于读取二进制数据,fwrite函数用于写入二进制数据,其原型为:
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);
fflush函数迫使一个输出流的缓冲区内的数据进行物理写入,不管它是不是已经写满,原型为:
int fflush(FILE *stream);
当我们需要立即把输出缓冲区的数据进行物理写入时,应该使用这个函数。
随机访问是通过在读取或写入先前定位到文件中需要的位置来实现的,有两个函数用于执行这项操作,其原型为:
long ftell(FILE *stream);
int fseek(FILE *stream, long offset, int from);
ftell函数返回流的当前位置,也就是说,下一个读取或写入将要开始的位置距离文件起始位置的偏移量。这个函数允许你保存一个文件的当前位置,这样你可能在将来会返回这个位置。在二进制流中,这个值就是当前位置距离文件起始位置之间的字节数。
fseek函数允许你在一个流中定位,这个操作将改变下一个读取或写入操作的位置。它的第1个参数是需要改变的流,第2和第3个参数标识文件中需要定位的位置。
用fseek改变一个流的位置会带来3个副作用。首先,行末指示字符被清除。其次,如果在fseek之前使用ungetc把一个字符返回到流中,那么这个被退回的字符会被丢弃,因为在定位操作以后,它不再是下一个字符。最后,定位允许你从写入模式切换到读取模式,或者回到打开的流以便更新。
rewind函数将读/写指针设置回指定流的起始位置,同时清除流的错误提示标志。
fgetpos和fsetpos函数分别是ftell和fseek函数的替代方案,它们的主要区别在于这对函数接受一个指向fpos_t的指针作为参数。fgetpos在这个位置存储文件的当前位置,fsetpos把文件位置设置为存储在这个位置的值。
改变缓冲方式的函数有:
void setbuf(FILE *stream, char *buf);
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
判断流状态的函数有:
int feof(FILE *stream); // 报告流是否处于文件尾
int ferror(FILE *stream); // 报告流的错误状态
void clearerr(FILE *stream); // 对指定流的错误标志进行重置
临时文件
为了方便,有时会使用一个文件来临时保存数据。当程序结束时,这个文件便被删除,因为它所包含的数据不再有用。tmpfile函数就是用于这个目的,其原型为:
FILE *tmpfile(void);
其创建一个文件,当文件被关闭或程序终止时这个文件便自动删除。该文件以wb+模式打开,使它可用于二进制和文本数据。
如果临时文件必须以其他模式打开或者由一个程序打开但由另一个程序读取,此时就不适合用tmpfile函数创建,而必须使用fopen函数,当结果文件不再需要时使用remove函数显式删除。
临时文件的文件名可用tmpname函数创建,其原型为:
char *tmpname(char *name);
如果传递给函数的参数为NULL,那么这个函数便返回一个指向静态数组的指针,该数组包含了被创建的文件名。否则,参数便假定是一个指向长度至少为L_tmpnam的字符数组的指针。无论哪种情况,这个被创建的文件名保证在调用不超过TMP_MAX次前,不会与已经存在的文件名同名。
remove函数删除一个指定的文件。如果当remove被调用时文件处于打开状态,其结果则取决于编译器,其原型为:
int remove(char const *filename);
rename函数用于改变一个文件的名字,如果已经有一个名为newname的文件存在,其结果取决于编译器。如果这个函数失败,文件仍然可以用原来的名字进行访问。其原型为:
int rename(char const *oldname, char const *newname);