我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。
我们⼀般谈的文件有两种:程序文件、数据文件(从⽂件功能的⻆度来分类 的)。程序文件包括源程序文件(后缀为.c),吗,目标文件(windows环境后缀为.obj),可执行程序(windows 环境后缀为.exe)。
根据数据的组织形式,数据文件被称为文本文件或者⼆进制文件。 数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存,就是⼆进制文件。 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是文本文件。
为C语⾔程序在启动的时候,默认打开了3个流:
• stdin - 标准输⼊流,在大多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
• stdout - 标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出 流中。
• stderr - 标准错误流,大多数环境中输出到显示器界面。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为文件指针。
C语⾔中,就是通过 FILE* 的文件指针来维护流的各种操作的。
FILE* pf;//文件指针变量
定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
ANSIC 规定使⽤ fopen 函数来打开文件, fclose 来关闭文件。
FILE * pFile;
//打开文件
pFile = fopen ("myfile.txt","w");
//文件操作
if (pFile!=NULL) {
fputs ("fopen example",pFile);
//关闭文件
fclose (pFile);
}
struct stu {
char name[20];
int age;
float score;
};
//写文件
struct stu s = { "zhangsan",20,90.5f };
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
fprintf(pf, "%s %s %.1f", s.name, s.age, s.score);
//读文件
struct stu s = { 0 };
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
fscanf(pf, "%s %s %f", s.name, &(s.age), &(s.score));//格式化输入
fprintf(stdout, "%s %s %.1f", s.name, s.age, s.score);//格式化输出
二进制读写文件:
struct stu s = { "zhangsan",20,90.5f };
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL) {
perror("fopen");
return 1;
}
//二进制写文件
fwrite(&s,sizeof(s),1,pf);
struct stu s = { 0 };
FILE* pf = fopen("data.txt", "rb");
if (pf == NULL) {
perror("fopen");
return 1;
}
//二进制读文件
fread(&s, sizeof(s), 1, pf);//读到了返回1,没读到返回0
printf("%s %s %.1f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
我们也可以用读写来实现文件拷贝,比如:
//从data.txt中读取数据写到data_copy.txt文件中
FILE* pfread = fopen("./data.txt", "r");
if (!pfread) {
perror("fopen->data.txt");
return 1;
}
FILE* pfwrite = fopen("data_copy.txt", "w");
if (pfwrite == NULL) {
fclose(pfread);
pfread = NULL;
perror("fopen->data_copy.txt");
return 1;
}
}
//拷贝
int ch = 0;
while ((ch = fgetc(pfread)) != EOF) {
fputc(ch, pfwrite);//字符输出
}
fclose(pfread);
pfread = NULL;
fclose(pfwrite);
pfwrite = NULL;
fseek 根据文件指针的位置和偏移量来定位文件指针。
ftell 返回文件指针相对于起始位置的偏移量
rewind 让文件指针的位置回到文件的起始位置
举例:
FILE* pf = fopen("data.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
//根据文件指针的位置和偏移量来定位文件指针
fseek(pf, 2, SEEK_CUR);//SEEK_CUR表示文件的相对当前位置
//定位文件指针到f
//fseek(pf, 5, SEEK_SET);//SEEK_SET表示文件的相对起始位置
//fseek(pf, -4, SEEK_END);//SEEK_END表示文件的相对结束位置
ch = fgetc(pf);
printf("%c\n", ch);
int pos = ftell(pf);
printf("%d\n", pos);//返回文件指针相对于起始位置的偏移量
rewind(pf);//让文件指针的位置回到文件的起始位置
fclose(pf);
pf = NULL;
在文件读取过程中,不能⽤feof函数的返回值直接来判断文件的是否结束。
feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。
比如:
FILE* pfwrite = fopen("data_copy.txt", "w");
if (pfwrite == NULL) {
fclose(pfread);
pfread = NULL;
perror("fopen->data_copy.txt");
return 1;
}
}
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
因为有缓冲区的存在,C语言在操作文件的时候,需要刷新缓冲区或者文件操作结束时关闭文件,不然会导致数据丢失。
所谓缓冲文件系统是指系统⾃动地在内存中为 程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”。
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘文件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
比如:
FILE* pf = fopen("test.txt", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);//睡眠10秒,Sleep()函数需要包含windows.h头文件
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
printf("再睡眠10秒,此时打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);//fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
学会使用文件可以帮助我们保存数据,方便下次再输入和记录数据。如果觉得这篇文章对你有帮助可以收藏下来,欢迎大家进行批评指正,一起加油