C语言中对文件操作的小结

在C语言中,文件的操作是通过FILE结构体进行了,具体实现时,先利用fopen返回一个指向FILE结构体的指针:
FILE *fopen( const char *filename, const char *mode );
filename:文件名,mode:打开的模式,规定了是可读、可写、追加之类的属性。
"r":可读,如果文件不存在,fopen调用失败
"w":可写,如果文件存在,那么原来的内容会被销毁。
"a":在文件尾追加,在新的数据写到文件里之前,不改变EOF标记,如果文件不存在,创建一个新的文件。
"r+":可读可写,文件必须存在。
"w+":打开一个空文件用来读写,如果文件存在,则内容被销毁。
"a+":可读可追加,在新的数据写到文件里之前,改变EOF标记;如果文件不存在,创建一个新的文件。
如果调用失败,返回一个空指针。

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
buffer:写入文件的内容;size:每一项的大小;count:写入了多少项;stream:指向文件的指针。返回值为写入的总的字节数。

先看一个简单的例子:

int main()
{
	FILE *pFile = fopen("1.txt","w");
	fwrite("hello,world!",1,strlen("hello,world!"),pFile);
	return 0;
}
这个程序看似平平,但还是大有文章可做:如果单步调试的话,执行完pFile后,就会在程序对应的文件夹下面产生一个名为“1.txt”的文件(大小为0);执行完fwrite后,大小还是为0,;只有当程序完全运行完以后,hello,world!才会被写入文件。这是为什么呢?因为C语言对文件的操作使用了缓冲文件系统:为每个正在使用的文件开辟了一段内存,当我们向硬盘上写数据时,是先写到内存里的(这与兼容DC有异曲同工之妙),直到内存满了,或者是我们通知系统要关闭这个文件了,才把内存里的数据拷贝到硬盘上。为什么这么设计呢?因为内存之间的操作速度要远远快于内存到硬盘的速度。如果我们每写一个字符后就把它保存在硬盘上,代价太大了。回过头来继续看,当整个程序执行完后,关闭这个文件,此时,才会把内存里的数据拷贝到硬盘上。


其实,我们完全可以手动的关闭文件:
int fclose( FILE *stream );
stream:指向文件的指针。如果成功,返回0;失败返回EOF。这样,当执行完关闭文件后,文件里面就有值了。


有时候,我们会反复读写一个文件,而且每次读写后都希望立即看到结果。这时候每次读完就关闭,然后重新打开的话实在太麻烦了,有没有简单的办法呢?可以使用fflush来刷新流:
int fflush( FILE *stream );
stream:指向文件的指针。


如果我们在接着向文件中写入数据:
fwrite("欢迎访问",1,strlen("欢迎访问"),pFile);
我们会发现,新写入的数据会在原来文件的末尾后加上。可系统是如何知道原来文件的末尾在哪里呢?
我们先看看FILE结构体:

struct _iobuf {
        char *_ptr;	//文件输入的下一个位置 
        int   _cnt;	//当前缓冲区的相对位置 
        char *_base;	//指基础位置(应该是文件的其始位置) 
        int   _flag;	//文件标志
        int   _file;	//文件的有效性验证
        int   _charbuf;	//检查缓冲区状况,如果无缓冲区则不读取
        int   _bufsiz;	//文件的大小
        char *_tmpfname;//临时文件名
        };
typedef struct _iobuf FILE;
注意,这只是VS2010中对FILE的实现!标准库中并没有规定FILE中必须是什么样的,之规定了我们可通过哪些函数去调用、访问它。但是从这个实例中,我们可以看出:结构体的第一个成员是一个指向文件输出的下一个位置的指针,我们可以通过fseek来移动文件的指针,它指向文件下一个要写入的位置:

int fseek( FILE *stream, long offset, int origin );
stream:指向文件的指针,offset:偏移量;origin:初始位置,它有3种取法:
SEEK_CUR:当前位置
SEEK_END:文件尾
SEEK_SET:文件头


如果我们这样使用:

	fwrite("hello,world!",1,strlen("hello,world!"),pFile);
	fflush(pFile);
	fseek(pFile,0,SEEK_SET);
	fwrite("欢迎访问",1,strlen("欢迎访问"),pFile);
那么文件中的内容就变为了:“欢迎访问rld!”

我们使用ftell函数获取当前文件指针的位置:
long ftell( FILE *stream );
返回值为与文件头的偏移量。
通过它,我们可以获得文件的长度。

读取文件使用的是fread函数:
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
buffer指明了读取的文件储存在哪里,size表明每个项的大小,count表明了读多少项,stream是指向FILE类型的指针。返回值为实际读取的字节数。

当然,在你读取文件之前,最好确保文件指针指向的是文件的头部,通过rewind函数来让指针复位:
void rewind( FILE *stream );
stream:指向文件的指针
下面看一个综合的例子:
int main()
{
	FILE *pFile = fopen("1.txt","w+");
	fwrite("hello,world!",1,strlen("hello,world!"),pFile);
	fflush(pFile);
	fseek(pFile,0,SEEK_SET);		//文件指针设为起点,写操作覆盖原来的内容
	fwrite("欢迎访问",1,strlen("欢迎访问"),pFile);
	fseek(pFile,0,SEEK_END);		//文件指针设为终点
	int len = ftell(pFile);			//获取文件字节数
	char* ch = (char*)malloc(sizeof(char)* (len+1));	//分配内存,多一个字节
	memset(ch,0,(len+1));			//清0
//	fseek(pFile,0,SEEK_SET);		//文件指针指向头部
	rewind(pFile);
	fread(ch,1,len,pFile);			//读取文件
	fclose(pFile);
	printf("%s",ch);				
	return 0;
}
需要说明一点,因为%s是遇到一个空字符串后才停止的,所以我们分配的内存比文件的字节数多了一个,然后全部清0,这样完成fread以后,剩下的那个字节刚好为0,用来表示字符串的结束。




基本的内容就是这么多,下面看一个细节问题:

int main()
{
	FILE *pFile = fopen("2.txt","w+");
	char ch[3];
	ch[0] = 'a';
	ch[1] = 10;		
	ch[2] = 'b';
	fwrite(ch,1,3,pFile);
	fflush(pFile);
	char buf[100];
	memset(buf,0,100);
	rewind(pFile);
	fread(buf,1,3,pFile);
	fclose(pFile);
	printf("%s",buf);
	return 0;
}
我们明明写了3个字符,为什么文件大小会是4个字节呢?
我们看看文件的16进制:61 0D 0A 62 ,其中61、62对应的是a和b,0A对应的是10,那么0D对应的是什么呢?答案是回车字符,这个字符是系统自动加进去的。而在读取文件时,我们也并没有读取个字节,只用读取3个字节,就能正确获取内容了。
与这个问题相关的一组概念是:二进制文件和文本文件。C语言中,默认是以文本的方式打开文件的,如果我们使用二进制文件方式打开:
FILE *pFile = fopen("2.txt","w+b");
也不会出什么问题,只不过文件的大小为3个字节,对应的16进制为:61 0A 62。但是如果你在写入时使用的是文本文件,而读取时使用的是二进制文件,就会出错,因为它会把回车当做一个字符输出。总而言之,读和写的方式要对应。





  • 9
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值