为什么要有这么多种格式的文件呢?
原因很简单,它们各有各的用途,区分就在于这些文件里面存放的数据集合所遵循的存储规则不一样。
文件无非就是一段数据的集合,这些数据可以是有规则的集合,也可以是无序的集合。
具有一定格式规则的文件一般是二进制存储的。
操作系统是以文件为单位对数据进行管理的。
也就是说,要访问外部介质上的数据,必须先按照文件名进行查找,然后从该文件中读取数据。要想写数据到外部介质,必须得建立一个文件,然后再写入。
在ANSI C标准中,使用的是“缓冲文件系统”。所谓缓冲文件系统指系统自动地在内存区为每一个正在使用的文件名开辟一个缓冲区,从内存向磁盘输出数据必须先送到内存中的缓冲区,装满后再一起送到磁盘去。反向也是如此。这里需要说明两个词:“输入”“输出”。输入表示从文件里读数据到程序里,输出表示从程序里写数据到文件中。
绝大多数流是完全缓冲的,这意味着“读取”和“写入”实际上是从是从缓冲区的内存区域来回复制数据。从内存区来回复制数据是非常快速的。用于输出流的缓冲区只有当它被写满是才会被刷新(flush,物理写入)到设备或文件中。
此时会出现一个问题:即当想要即刻把内容打印到屏幕上时,因为编译器的不同(只有当操作系统可以断定他们与交互设备并无联系时才会进行完全缓冲。否则,他们的缓冲状态将因编译期而异)。
解决方法为:把标准输出和标准输入联系在一起,就是当请求输入时,同时刷新输出缓存区。
printf("something or other");
fflush(stdout);
这样就可以在用户输入之前,提示用户进行输入的信息和以前写入到缓冲区的内容将出现在屏幕上。
注:调试程序时用 printf( )
,可能会因为缓冲输出而出现位置不正确的结论。解决方法为在每个用于调试的prinitf()后都加一个 fflush ( sdout );
字符I/O
当一个流被打开之后,他可以用于输入和输出。他的最简单的形式是I/O。字符输入由getchar函数加u走执行。
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
需要操作的流作为参数传递给getc和fgetc,但是getchar是重视从标准输入中读取。每个函数从流中读取下一字符,并把它作为函数的返回值返回。如果流中不存在更多的字符,函数就返回常量值EOF(-1)。
注:可用stdio.h头文件里的perror函数来报告错误。
perror 函数原型为 :
void perror (char const *message);
#include <stdio.h>
int main(void)
{
FILE *fp;
fp = fopen("/root/noexitfile","r+");
if(NULL == fp)
{
perror("/root/noexitfile");
}
return 0;
}
输出结果为
你应该始终检查fopen函数的返回值!
如果函数失败,它将会返回一个NULL值,如果程序不检查错误,就会将NULL指针传给后续的I/O函数,他们将对这个指针执行间接访问,并将失败。
perror(s)用来将上一个函数发生的错误原因输出到标准设备(stderr)。参数s所指的字符串会先打印出,后面加上错误原因字符串。
在读取文件名或者从命令行接受文件名的程序中,报告错误是必要的。
在库函数中有个叫errno变量,每个errno变量值对应着以字符串表示着的错误类型。当你调用“某些”函数出错时,该函数已经重新设置了errno的值。perror函数只是将你输入的一些信息和现在的errno所对应的错误一起输出。
EOF是很多函数的返回值,他提示到了文件尾。EOF所选择的实际值比一般字符多几位这是为了避免二进制数被误认为EOF。
关闭流
流是用fclose 关闭的,原型如下:
int fclose( FILE *f );
对于输出流,fclose函数在文件关闭之前刷新缓冲区。如果它执行成功,fclose返回o,否则返回EOF。
注意对操作失败进行检查:
//关闭文件(期望在这里不会发生什么错误)
if(fclose(fp)!=0)
{
perror("fclose");
exit(EXIT_FAILURE);
}
return exit_status;
因为 fp变量可能因为 fopen 和 fclose 之间的一个程序bug而发生修改。这个bug无疑将导致程序失败。在那些并不检查fopen返回值的程序中fp的值甚至可能是NULL,在任何一种情况下,fclose 都会失败,而且程序可能在进行fclose之前就已经终止了。所以要进行错误检查。