大数据算法,分布式技术,spark技术爱好者
分类: C/C++
2013-07-28 21:18:40
很多人觉得自己已经很熟悉C文件操作了,而网上的各种文档也大量的介绍了C文件读写操作。但是我没有发现有把fopen的最后一个参数flag的细节和实现机理将的很透彻的。希望我通过一个晚上的试验和总结得到的如下结果可以给大家提供一个完美的C文件读写操作解决方案。
函数fopen的最后一个flag可以是r,w,a,r+,r+,a+。所有上边的属性在某些系统中是需要加b来专门处理二进制文件操作的,但是在linux系统中,Posix标准已经忽略了b的处理,本文讲的是UC编程,所以完全没有必要理会b。下面对每个flag进行讲解,最后重点讲a+,r+,w+。
先看w,r的区别:
w:打开并赋予写权限,没有文件时创建文件,有文件时truncate(清空)源文件
r:打开并赋予读权限,没有文件时返回NULL指针标示错误,errorno设为“can't find file”对应的错误码。
再来看w+和r+:
都赋予文件读写权限,读写指针均从文件开头开始.区别就是当找不到文件时是否创建文件.
再来详细看看a和a+:
以a打开的文件,开始写时读写指针在文件尾,但是这个指针是不能进行读操作的,会返回无效数据。
技巧:我们可以用a打开文件,直接ftell得到文件大小。而a+则不可以,下面就具体解释。
a+打开后读写指针在什么位置呢?下面将通过试验阐述a+文件内位置指针实现的根本机理:
看下面的代码:
#include
#include
typedef struct
{
int id;
char name[20];
float salary;
}emp;
int main()
{
emp rec1 = {1, "zz", 1200.5};
emp rec2 = {2, "zaa", 1200.5};
emp rec = {};
FILE *fp = fopen("emp.dat","a+");
if (fp == NULL)
{
perror("fopen:");
exit(-1);
}
printf("%ld\n",ftell(fp));
//fseek(fp,0,SEEK_END);
//fwrite(&rec1, sizeof(emp), 1, fp);
//fwrite(&rec2, sizeof(emp), 1, fp);
printf("%ld\n",ftell(fp));
//fseek(fp,-28,SEEK_END);
//printf("%ld\n",ftell(fp));
fread(&rec, sizeof(emp), 1, fp);
printf("%d %s %f\n", rec.id, rec.name, rec.salary);
printf("%ld\n",ftell(fp));
exit(0);
}
结果如下:
结果显示:在a+下读取了文件第一个人员信息,读完后文件指针指向28。因为结构体大小是28。很多同学要问,a+打开指针不是在文件尾么?事实上此时位置指针并没有赋值,当你调用fread时,还是会从文件头读取。只有当你调用fwrite时,会把当前指针位置置为文件尾。所以再读什么也读不到了,只有通过fseek才能移动位置指针,继续进行有效的读取。
这就是a+特殊的内部实现机理,因为a+的特殊性而决定的。这种特殊性也就决定了,续写文件时,a+选项是不能边写边修改的,feek指针不能影响fwrite,不能进行复写修改!
而w+和r+则可以进行复写修改!!但是w+会覆盖原文件。
而r+不存在指针跳来跳去的难以控制性,会使代码更容易调试和理解。而且只需要fseek(fp,0,SEEK_END)就可以追加。所以在不考虑安全性的前提下最好的打开文件的方式: r+。
至于文件是不是存在,完全可以用fwrite进行新建后关掉文件后再调用带r+选项的fopen。然后就可以进行随意的读写操作了,控制fseek就像控制鼠标一样。
下面作个简单的模板实现:
#include
#include
typedef struct
{
int id;
char name[20];
float salary;
}emp;
int main()
{
emp rec1 = {1, "zz", 1200.5};
emp rec2 = {2, "zaa", 1200.5};
emp rec = {};
FILE *fp = fopen("emp.dat","a+");//if create, use "w+"
if (fp == NULL)
{
perror("fopen:");
exit(-1);
}
fclose(fp);
fp = fopen("emp.dat","r+");
printf("%ld\n",ftell(fp));
//写3个
fwrite(&rec1,sizeof(rec1),1,fp);
fwrite(&rec2,sizeof(rec2),1,fp);
fwrite(&rec2,sizeof(rec2),1,fp);
//倒回2个
fseek(fp,-56,SEEK_CUR);
//复写一个
fwrite(&rec1, sizeof(emp), 1, fp);
printf("%ld\n",ftell(fp));
//读最后一个,打印
fread(&rec, sizeof(emp), 1, fp);
printf("%d %s %f\n", rec.id, rec.name, rec.salary);
exit(0);
}
结果:
文件:
可以看到第二个单元被成功复写,而第三个单元也被成功打印。没有任何问题!