为什么会有文件操作
那么大家可能会问:为什么会有文件操作呢?前面我们可能都了解了通讯录,我们知道当我们使用通讯录的时候我们可以添加联系人,也可以删除联系人,但是当我们退出程序之后下次再进来的时候,我们要想看看通讯录里面有那些联系人,我们打开通讯录发现里面是空的,这是为什么呢?因为我们这种通讯录实在内存上操作的,当我们关闭程序或者关机的时候,这些内存就会被释放掉了,那么我们想要下次打开通讯录的时候,里面的内容还在的话,我们就需要使用文件来操作了。
什么是文件
文件通常是指磁盘上的文件,就像我们平时购买电脑的时候我们可能会买16+512的,这个16通常是指运行内存,我们写代码的时候通常使用的就是这个内存,而我们安装QQ、微信啊这些就是安装在磁盘上的,这些数据不会随着你电脑关机或者没电的时候而消失,他会一直存在,除非你故意删除或者磁盘损坏。所以我们使用文件来操作可以将数据存储在磁盘上保存。
文件又分为程序文件文件和内存文件。他们分类的依据是根据功能来分类的,不知道你们是否发现了,当我们写了一个代码并编译运行的时候,我们可以在我们项目的文件路径下发现.obj后缀和.exe后缀的文件,这些等等都属于程序文件,而当我们程序运行的时候读取的文件称为数据文件。
那么我们知道了什么是文件以及使用文件的好处了之后,我们来学习怎样进行文件操作。
文件操作
文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。也就是说我们需要使用FILE来找到文件的地址,并对其进行相关操作。其实这就跟普通指针差不多,就像int* p = &a,一样*说明他是一个指针变量,而int说明这个指针指向的是int类型的数据,FILE也是如此。
文件的打开与关闭
当我们使用文件的时候我们需要在知道使用的步骤,就像我们想要在冰箱里面拿东西一样,我们首先需要打开冰箱,然后我们可以从冰箱里面拿出东西也可以往冰箱里面放东西,我们进行文件操作的时候也是先打开文件,然后进行相关操作,最后就是关闭文件。
fopen(打开文件)
FILE * fopen ( const char * filename, const char * mode );
const char* filename是我们要打开的文件地址,const char* mode是我们以怎样的形式打开。
fclose(关闭文件)
int fclose ( FILE * stream );
FILE * stream是我们需要关闭的文件地址,这里我们需要注意的是fclose并不会将要关闭的文件置为NULL,所以我们需要手动置空。
接下来我们就来看看有哪些打开文件的方式吧。
打开文件的方式
我们在使用的时候可以根据需要以不同的方式打开。我们举个例子:
首先我们在我们的项目路径下创建一个test.txt文档,用来做测试。
然后我们在这个文档里面写入内容。
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); } //进行相关文件操作 //关闭文件 fclose(pf); pf = NULL; return 0; }
我们这里就是以读的形式打开的。
文件的顺序读写
这里的流是什么意思呢?其实这个流只是一个抽象概念,就像一条河流,当我们需要灌溉农田的时候我们就可以用抽水机来抽取。我们的键盘和屏幕实际上也是一个文件,我们的电脑从键盘中读取数据,然后将数据显示到屏幕上,键盘也叫标准输入流(stdin),屏幕叫做标准输出流(stdout)。
那么我们来举几个例子来看看这写函数是什么作用吧。
fgets函数
char * fgets ( char * str, int num, FILE * stream );
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "r"); char arr[10] = { 0 }; if (pf == NULL) { perror("fopen"); return 0; } //进行相关文件操作 fgets(arr, 5, pf); printf("%s", arr); //关闭文件 fclose(pf); pf = NULL; return 0; }
虽然我们在这里想要读取5个元素,但是其实只读取了4个元素,因为最后一个元素要读取’\0’。
fputc函数
int fputs ( const char * str, FILE * stream );
我们先将前面的文档里面的内容删除。
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //写文件 //把26个字母写到文件中 int i = 0; for (i = 0; i < 26; i++) { fputc('a'+i, pf); } //关闭文件 fclose(pf); pf = NULL; return 0; }
fgets函数
char * fgets ( char * str, int num, FILE * stream );
fgets可以读取一行的数据
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //读 char arr[20]; fgets(arr, 5, pf); printf("%s\n", arr); //关闭文件 fclose(pf); pf = NULL; return 0; }
fputs函数
fputs写一行的数据
int fputs ( const char * str, FILE * stream );
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } fputs("hello bit", pf); //关闭文件 fclose(pf); pf = NULL; return 0; }
fprintf函数
int fprintf ( FILE * stream, const char * format, ... );
后面的省略号说明你自己可以定义长度。
#include<stdio.h> struct S { int n; float f; char arr[20]; }; int main() { struct S s = { 100, 3.14f, "zhangsan" }; //打开文件 FILE* pf = fopen("test.txt", "w"); if (NULL == pf) { perror("fopen"); return 1; } //写文件 fprintf(pf, "%d %f %s\n", s.n, s.f, s.arr); //关闭文件 fclose(pf); pf = NULL; return 0; }
fscanf函数
int fscanf ( FILE * stream, const char * format, ... );
#include<stdio.h> struct S { int n; float f; char arr[20]; }; int main() { struct S s = {0}; //打开文件 FILE* pf = fopen("test.txt", "r"); if (NULL == pf) { perror("fopen"); return 1; } //读文件 fscanf(pf, "%d %f %s", &(s.n), &(s.f), s.arr); printf("%d %f %s\n", s.n, s.f, s.arr); //关闭文件 fclose(pf); pf = NULL; return 0; }
那么前面的都是顺序读写,但是当我们想要在任意位置写的时候我们又该怎么办呢?
我们这就得用到fseek函数和ftell函数了,这两个函数可以随机读写文件。
文件的非顺序读写
fseek函数
int fseek ( FILE * stream, long int offset, int origin );
stram还是我们需要读取的文件的地址,offset 是相对于origin的偏移量,origin有三种不同的形式。SET表示文件的开始位置,CUR表示文件指针的当前位置,END表示文件的结尾。
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen file"); return 1; } for (int i = 0; i < 5; i++) { fgetc(pf); } //现在pf文件指针已经向后移动了5个字节,现在我们想要读取的元素是b,b与文件首元素的地址的偏移量是1 fseek(pf, 1, SEEK_SET); int a = fgetc(pf); printf("%c", a); fclose(pf); pf = NULL; return 0; }
ftell函数
long int ftell ( FILE * stream );
ftell函数返回的是你当前的文件指针与文件开始的地址处的偏移量,这里我就不需要过多解释了。
rewind函数
void rewind ( FILE * stream );
这个函数的作用是让文件指针回到文件的起始位置
二进制读写
fwrite函数`
fwrite函数是针对二进制来说的,他写入的形式是以二进制来写的。
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
这个函数返回的是你写入的字符的个数,参数一个是ptr,代表的是从哪写入,一个是size,表明要写入的内容的大小,count是指要写入多少个这样的内容,stream是你要写入那个文件中。
#include<stdio.h> struct S { int n; float f; char arr[20]; }; int main() { struct S s = { 10,3.14f,"hello" }; FILE* pf = fopen("test.txt", "wb"); if (pf == NULL) { perror("fopen"); return 1; } fwrite(&s, sizeof(struct S), 1, pf); fclose(pf); pf = NULL; return 0; }
这样我们可能看不懂吧,因为这是以二进制的形式写入的,所以我们并不能看懂。
fread函数
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
#include <stdio.h> struct S { char name[20]; int age; float score; }; int main() { struct S s = {0}; FILE* pf = fopen("test.txt", "rb"); if (pf == NULL) { perror("fopen"); return 1; } //读文件 fread(&s, sizeof(struct S), 1, pf); printf("%s %d %f\n", s.name, s.age, s.score); //关闭文件 fclose(pf); pf = NULL; return 0; }
结语
那么这些就是我学到的关于文件操作的只是,感谢大家的观看,如有错误,请随时订正。