【参考课程:B站 BV1Vm4y1r7jY】P155-160
1.为什么使用文件
使用文件可以将数据直接存放在电脑的硬盘上,做到了数据的持久化
2.什么是文件
磁盘上的文件是文件
但是在程序设计中,我们一般谈的文件有两种(从文件的功能角度分类):程序文件、数据文件
2.1程序文件
包括源程序文件(.c)
目标文件(Windows环境后缀为.obj)
可执行程序(Windows环境后缀为.exe)
2.2数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件
写文件:内存→文件
读文件:文件→内存
2.3文件名
一个文件要有一个唯一的文件标识,以便于用户识别和引用
文件名包含3部分:文件路径+文件名主干+文件后缀 E.g. c:\code\test.txt
为了方便起见,文件标识常被称为文件名
3.文件的打开和关闭
3.1文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”
文件指针:在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息
→这些信息保存在一个结构体变量中
*该结构体类型是有系统声明的,取名为FILE. //声明在<stdio.h>中
#ifndef _FILE_DEFINED
#define _FILE_DEFINED
typedef struct _iobuf
{
void* _Placeholder;
} FILE;
#endif
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息。(使用者不必关心细节)
一般通过一个FILE指针来维护这个FILE结构的变量
创建一个FILE*的指针变量:
#include<stdio.h> FILE* pf; //文件指针变量
pf是一个指向FILE类型数据的指针变量
可以使pf指向某个文件的文件信息区(是一个结构体变量),通过该文件信息区中的信息就能访问该文件
*通过文件指针变量能找到与它关联的文件。
3.2文件的打开和关闭
打开fopen( )
w 写文件会将文件内容全部清空后再写内容
a 不会覆盖原文件内容
关闭fclose( )
#include<stdio.h> #include<string.h> //包含strerror() int main() { FILE* pf; //打开文件 pf = fopen("test.txt", "r"); //r-读文件需要文件存在 if (pf == NULL) //判断文件是否存在 //文件不存在指针会指向空指针 { printf("%s\n", strerror(errno));//打印未能成功打开文件的原因 return 1; } //关闭文件 fclose(pf);//记得要关闭文件,∵程序可打开的文件数量有限;不关闭文件可能会丢失文件数据 pf = NULL;//重置文件指针 return 0; }
文件不存在打印:
在该程序文件路径下存在test.txt时文件会读取成功
相对路径/绝对路径
//相对路径 pf = fopen("test.txt", "r"); //打开当前目录下的test.txt文件 //绝对路径 pf = fopen("C:\\Users\\achir\\Desktop\\test.txt", "r"); //打开指定路径下的test.txt文件
4.文件的顺序读写
fputc( )
#include<stdio.h> #include<string.h> int main() { //打开文件 FILE* pf; pf = fopen("test.txt", "w"); if (pf == NULL) //判断文件是否存在 { perror("fopen"); //等价于printf("%s\n", strerror(errno)) return 1; } ///***写文件***/// fputc('a', pf); //写入一个字符 //关闭文件 fclose(pf); //pf = NULL;//重置文件指针 return 0; }
perror("fopen"); 打印:
fgetc( )
#include<stdio.h> #include<string.h> int main() { //打开文件 FILE* pf; pf = fopen("test.txt", "r"); if (pf == NULL) //判断文件是否存在 { perror("fopen"); return 1; } ///***读文件***/// int ch = 0; while ((ch = fgetc(pf)) != EOF) //fgetc按顺序一个一个向后读取字符,能换行读 { //读取完毕不能再读时fgetc()返回EOF printf("%c", ch); //此循环可将文件字符按顺序全部打印 } //关闭文件 fclose(pf); //pf = NULL;//重置文件指针 return 0; }
fputs( )
#include<stdio.h> #include<string.h> int main() { FILE* pf; //w-会覆盖原内容(在打开文件时即将内容清空) pf = fopen("test.txt", "a"); //a-在原内容后追加 if (pf == NULL) { perror("fopen"); return 1; } ///***写文件***/// fputs("hello", pf); //写一行内容 //关闭文件 fclose(pf); //pf = NULL;//重置文件指针 return 0; }
fgets( )
#include<stdio.h> #include<string.h> int main() { FILE* pf; pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } ///***读文件***/// char ch[20]; //创建字符数组接受读取内容 fgets(ch, 9, pf); //从【pf】指向的文件中读取【9】个字符,将读到的内容存在数组【ch】中 printf("%s\n", ch);//可将ch打印出来观察 //关闭文件 fclose(pf); pf = NULL; return 0; }
- 读取的字符个数中包括\0,真正有效读取字符-1
即:用fgets(ch, 2, pf)读abcd,则得到ch[20]={'a','\0'}- fgets( )只能读同一行上的字符
fprintf( )
格式化的向文件写入信息
#include<stdio.h> #include<string.h> struct S { char name[10]; int a; float f; }; int main() { struct S s1 = { "ergo",4,6.6 }; FILE* pf; pf = fopen("test.txt", "w"); fprintf(pf,"%s %d %f",s1.name,s1.a,s1.f); //格式化的把s1的内容写进pf指向的文件test.txt中 fclose(pf); pf = NULL; return 0; }
fscanf( )
格式化的从文件读取信息
#include<stdio.h> #include<string.h> struct S { char name[10]; int a; float f; }s1,s2; int main() { FILE* pf; pf = fopen("test.txt","r"); if (pf == NULL) { perror("fopen"); return 1; } fscanf(pf, "%s %d %f", &(s1.name), &(s1.a), &(s1.f)); fscanf(pf, "%s %d %f", &(s2.name), &(s2.a), &(s2.f)); //一行一行按顺序的格式化读取文件内容 printf("%s %d %f\n", s1.name, s1.a, s1.f); printf("%s %d %f\n", s2.name, s2.a, s2.f); fclose(pf); pf = NULL; return 0; }
fwrite( )
fread( )
*文件的顺序读写默认从文件开头开始一一读取
5.文件的随机读写
fseek( )
根据文件指针的起始位置origin和偏移量offset来定位文件指针
#include<stdio.h> #include<string.h> int main() { FILE* pf; pf = fopen("test.txt", "r"); //test.txt内容: if (pf == NULL) //abcdefg { perror("fopen"); return 1; } //读文件 //定位文件指针 [ fseek(pf, 2, SEEK_SET);//从文档头右偏移2位开始读 //位数从0开始 int ch = fgetc(pf); //[abcdefg → ab[cdefg //读完后变成 abc[defg printf("%c\n", ch); //c fseek(pf, 1, SEEK_CUR);//从当前指针位置右偏1位开始读 ch = fgetc(pf); //abc[defg → abcd[efg printf("%c\n", ch); //e fseek(pf, -1, SEEK_END);//从末尾左偏1位开始读 ch = fgetc(pf); //abcdefg[ → abcdef[g printf("%c\n", ch); //g return 0; }
ftell( )
返回文件指针相对起始位置的偏移量
fseek(pf, 2, SEEK_SET); printf("%d\n", ftell(pf)); //2 //test.txt内容: int ch = fgetc(pf); //abcdefg printf("%d\n", ftell(pf)); //3 fseek(pf, 1, SEEK_CUR); printf("%d\n", ftell(pf)); //4 fseek(pf, -1, SEEK_END); printf("%d\n", ftell(pf)); //6
rewind( )
让文件指针回到起始位置
fseek(pf, 2, SEEK_SET); printf("%d\n", ftell(pf)); //2 rewind(pf); printf("%d\n", ftell(pf)); //0
6.文本文件和二进制文件
7.文件读取结束的判定
feof( )
用于判定文件读取结束的原因是什么(是读取失败还是遇到文件末尾而读取结束),
不能用来判断文件是否结束!
//返回非零:读取成功结束
//返回0:读取失败
如何判断文件读取是否结束
- 文本文件读取是否结束,判断的返回值是否为EOF(fgetc),或NULL(fgets)
- 二进制文件的读取结束,判断返回值是否小于实际要读的个数