C语言常用文件操作

文章出处:http://blog.csdn.net/thefutureisour/article/details/8133931

在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:指向文件的指针。返回值为写入的总的字节数。

先看一个简单的例子:

  1. int main()  
  2. {  
  3.     FILE *pFile = fopen("1.txt","w");  
  4.     fwrite("hello,world!",1,strlen("hello,world!"),pFile);  
  5.     return 0;  
  6. }  
这个程序看似平平,但还是大有文章可做:如果单步调试的话,执行完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结构体:

  1. struct _iobuf {  
  2.         char *_ptr; //文件输入的下一个位置   
  3.         int   _cnt; //当前缓冲区的相对位置   
  4.         char *_base;    //指基础位置(应该是文件的其始位置)   
  5.         int   _flag;    //文件标志  
  6.         int   _file;    //文件的有效性验证  
  7.         int   _charbuf; //检查缓冲区状况,如果无缓冲区则不读取  
  8.         int   _bufsiz;  //文件的大小  
  9.         char *_tmpfname;//临时文件名  
  10.         };  
  11. 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:文件头


如果我们这样使用:

  1. fwrite("hello,world!",1,strlen("hello,world!"),pFile);  
  2. fflush(pFile);  
  3. fseek(pFile,0,SEEK_SET);  
  4. 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:指向文件的指针
下面看一个综合的例子:
  1. int main()  
  2. {  
  3.     FILE *pFile = fopen("1.txt","w+");  
  4.     fwrite("hello,world!",1,strlen("hello,world!"),pFile);  
  5.     fflush(pFile);  
  6.     fseek(pFile,0,SEEK_SET);        //文件指针设为起点,写操作覆盖原来的内容  
  7.     fwrite("欢迎访问",1,strlen("欢迎访问"),pFile);  
  8.     fseek(pFile,0,SEEK_END);        //文件指针设为终点  
  9.     int len = ftell(pFile);         //获取文件字节数  
  10.     char* ch = (char*)malloc(sizeof(char)* (len+1));    //分配内存,多一个字节  
  11.     memset(ch,0,(len+1));           //清0  
  12. //  fseek(pFile,0,SEEK_SET);        //文件指针指向头部  
  13.     rewind(pFile);  
  14.     fread(ch,1,len,pFile);          //读取文件  
  15.     fclose(pFile);  
  16.     printf("%s",ch);                  
  17.     return 0;  
  18. }  
需要说明一点,因为%s是遇到一个空字符串后才停止的,所以我们分配的内存比文件的字节数多了一个,然后全部清0,这样完成fread以后,剩下的那个字节刚好为0,用来表示字符串的结束。




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

  1. int main()  
  2. {  
  3.     FILE *pFile = fopen("2.txt","w+");  
  4.     char ch[3];  
  5.     ch[0] = 'a';  
  6.     ch[1] = 10;       
  7.     ch[2] = 'b';  
  8.     fwrite(ch,1,3,pFile);  
  9.     fflush(pFile);  
  10.     char buf[100];  
  11.     memset(buf,0,100);  
  12.     rewind(pFile);  
  13.     fread(buf,1,3,pFile);  
  14.     fclose(pFile);  
  15.     printf("%s",buf);  
  16.     return 0;  
  17. }  
我们明明写了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。但是如果你在写入时使用的是文本文件,而读取时使用的是二进制文件,就会出错,因为它会把回车当做一个字符输出。总而言之,读和写的方式要对应。




发布了5 篇原创文章 · 获赞 5 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章