文章出处: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:指向文件的指针。返回值为写入的总的字节数。
先看一个简单的例子:
- int main()
- {
- FILE *pFile = fopen("1.txt","w");
- fwrite("hello,world!",1,strlen("hello,world!"),pFile);
- return 0;
- }
其实,我们完全可以手动的关闭文件:
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;
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);
我们使用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;
- }
基本的内容就是这么多,下面看一个细节问题:
- 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;
- }
我们看看文件的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。但是如果你在写入时使用的是文本文件,而读取时使用的是二进制文件,就会出错,因为它会把回车当做一个字符输出。总而言之,读和写的方式要对应。