perror函数以一种简单、统一的方式报告错误信息。
例如,如果一个程序试图从一个并不存在的磁盘文件读取数据,操作系统除了提出发生了错误之外就没有什么好做的了。标准库函数在一个外部整形变量errno中保留错误代码之后就把这个信息传递给用户程序,提示操作失败的准确原因。
void perror(char const *message);
如果message不是NULL并且指向一个非空的字符串,perror函数就打印这个字符串,后面跟一个分号和一个空格,然后打印一条用于解释当前错误代码的信息。
终止执行
另一个有用的函数是exit,它用于终止一个程序的执行,它的原型定义于stdlib.h,如下所示:
void exit (int status);
status参数返回给操作系统,用于提示程序是否正常完成。这个值和main函数返回的整型状态相同,预定义符号EXIT_SUCCESS和EXIT_FAILURE分别提示程序的终止是成功还是失败,虽然程序也可以使用其他的值,但是他们的具体含义将取决于编译器。
用于输出流的缓冲区只有当它写满时才会被刷新(flush,物理写入)到设备或文件中。一次性把写满的缓冲区写入和逐片把程序产生的输出分别写入相比效率跟高。
尽管这种缓冲通常是我们所需要的,但当你调试程序是仍然可能引起混淆,一个厂家的调试策略是把一些printf函数的调用散布于程序中,确定错误出现的具体位置,但是,这些函数调用的输出结果被写入到缓冲区中,并不立即显示在屏幕上,事实上,如果程序失败,缓冲输出可能不会被实际写入,这就可能使程序员得到关于错误出现位置的不正确结论,这个问题的解决方法就是在每个用于调试的printf函数之后立即调用flush,如下所示:
printf("something or other");
fflush(stdout);
fflush函数迫使缓冲区的数据立即写入,不管它是否已满。
文本流
流分为两种类型,文本(text)流和二进制(binary)流。文本流的有些特性在不同的系统中可能不同,其中值之一就是文本行的最大长度,标准规定至少允许254个字符。另外一个可能不同的特性是文本行的结束方式。例如,在MS-DOS系统中,文本文件约定一个会车和一个换行符结尾,但是,UNIX系统只使用一个换行符结尾。
二进制流
另一方面,二进制流中的字节将完全根据程序编写它们的形式写入到文件或设备中,而且完全根据它们从文件或设备读取的形式读入到程序中,它们并为做任何改变。这种类型的流适用于非文本数据,但是如果你不希望I/O函数修改文本的行末字符也可以把它们用于文本文件。
打开文件流
FILE *fopen(char const *name, char const *mode);
读取 | 写入 | 添加 | |
文本 | 'r' | "w" | "a" |
二进制 | "rb" | "wb" | "ab" |
在mode中添加‘a+’表示该文件打开用于更新并且流既允许读也允许写。但是,如果你已经从该文件读入了一写数据,那么在你开始向他写入数据之前,你必须调用其中一个文件定位函数(fseek , fsetpos ,rewind)。在你向文件写入一些数据之后,如果你又想从文件读取一些数据,你首先必须调用fflush函数或者文件定位函数之一。
为格式化的行I/O
char *fgets(char *buffer, int buffer_size, FILE *stream);
char *fputs(char const *buffer, FILE *stream);
fgets从指定的stream读取字符并把它们复制到buffer中。当它读取一个换行符并存储到缓冲区之后就不再读取。如果缓冲区内存储的字符数达到buffer_size - 1个时它也停止读取。在这种情况下,并不会出现数据丢失的情况,因为下一次调用fgets将从流的下一个字符开始读取。在任何一种情况下,一个NUL字节将被添加到缓冲区所储存数据的末尾,使它成为一个字符串。
如果在任何字符读取就到达了文件末尾,缓冲区就未进行修改,fgets函数返回一个NULL 指针,否则,fget返回它的第一个参数(指向缓冲区的地址)。
传递费fputs的缓冲区必须包含一个字符串,它的字符被写入到流中,这个字符串预期已NUL字节结尾,所以这个函数没有一个缓冲区长度参数。这个字符串是逐字写入的,如果它不包含一个换行符,就不会写入换行符,如果他包含了好几个换行符,所有的换行符都会被写入,因此,当fgets每次都读取一整行时,fputs却可以一次写入一行的一部分,也可以一次写入一整行,甚至可以一次写入好几行。如果写入时出现了错误,fputs返回常量值EOF,否则它将返回个非负值。
格式化的行I/O
scanf家族
int fscanf(FILE *stream, char const *format, .......);
int scanf(char const *foramat,....);
int sscanf(char const *string, char const *format, ........);
这些函数都是从输入源读取字符并根据format字符串给出的格式代码对它们进行转换。fscanf的输入源就是作为参数给出的流,scanf从标准输入读取,而sscanf则从第一个参数所给出的字符串中读取字符。
当格式化字符到达末尾或者读取的输入不在匹配格式化字符串指定的类型时,输入就停止。在任何一种情况下,被转换的输入值的数目作为函数的返回值返回。如果在任何输入值被转换之前文件就已经达到末尾,函数就返回常量值EOF。
printf家族
int fprintf(FILE * stream, char const *foemat, .......);
int printf(char const *format, .....);
int sprintf(char *buffer, char const *format,....);
printf根据格式化代码和format参数中的其他字符对参数列表中的值进行格式化。使用风printf,可以使用任何输出流,而sprintf把它的结果作为一个NUL结尾的字符串存储到指定的buffer缓冲区而不是写入到流中,这3个函数的返回值是实际打印或存储的字符数。
long ftell(FILE *stream);
int fseek(FILE *stream, long offset, int from);
ftell函数返回流的当前位置,也就是说,下一个读取或写入将要开始的位置距离文件起始位置的偏移量,这个函数允许你保存一个文件的当前位置。这样你可能在将来会返回这个位置。在二进制流中,这个值就是当前距离文件起始位置之间的字节数。
在文本流中这个值表示一个位置,但它并不一定准确地表示当前位置和文件起始位之间的字符数,因为有些系统将对行末进行翻译转换,但是,ftell函数但会的值总是可以用于fseek函数中,作为一个距离文件起始位置的偏移量。
fseek函数允许你在一个流中定位。这个操作将改变下一个读取或写入操作的位置,它的第一个参数是需要改变的流,它的第二个和第三个参数标志文件中需要定位的位置,
fseek参数
如果from是…… | 你将定位到 |
SEEK_SET | 从流的起始位置起offset个字节,offset必须是一个非负数 |
SEEK_CUR | 从流的当前位置起offset个字节,offset的值可正可负 |
SEEK_END | 从流的尾部位置起offset个字节,offset的值可正可负,如果它是正值,它将定位到文件尾的后面 |
改变缓冲方式
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
setvbuf函数,mode参数用于指定缓冲的类型。__IOFEF指定一个完全缓冲的流,__IONBF指定一个不缓冲的流,__IOLBF 指定一个行缓冲流。所谓行缓冲流,就是每当一个换行符写入到缓冲区时,缓冲区便进行刷新。
buf和size参数用于指定须臾奥使用的缓冲区,如果buf为NULL,那么size的值必须是0.