文件操作详解
目录
1.为什么使用文件
2.什么是文件
2.1 程序文件
2.2 数据文件
2.3 文件名
3.二进制文件和文本文件
4.文件的打开和关闭
4.1 流和标准流
4.1.1 流
4.1.2 标准流
4.2 文件指针
4.3 文件的打开和关闭
5.文件的顺序读写
5.1 顺序读写函数介绍
5.1.1字符读/写函数
5.1.2.字符串读写函数
5.1.3.格式读/写函数
5.1.4二进制输入输出
5.2 对比一组函数
6.文件的随机读写
6.1fseek——读写位置定位函数
6.2ftell——读写位置函数
6.3rewind——文件头定位函数
7.文件读取结束的判定
7.1被错误使用的feof
8.文件缓冲区
宝子们!学C语言绕不开的文件操作来啦💻 想让程序里的数据不再一运行就消失,实现持久化存储,文件操作就是必备技能!不管是写日志、存配置,还是处理数据文件,都得靠它搞定。
但刚接触的小伙伴总被文件指针、打开模式、读写函数搞得头大,稍不注意就踩坑。这篇就把C语言文件操作的核心知识点掰开揉碎,从基础的打开关闭,到字符/行/块的读写实操,再到避坑小技巧,全程配简单易懂的代码示例,新手也能轻松拿捏,彻底告别文件操作烦恼~
1.为什么使用文件
如果没有文件,我们的程序存储在电脑内存中,如果程序退出,内存回收,数据就丢失了,如果要持久化保存,我们可以使用文件
2.什么是文件
磁盘(硬盘)上的文件都是文件,但在程序设计中,一般将文件分为程序文件和数据文件
2.1 程序文件
程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(后缀为.exe)
2.2 数据文件
文件的内容不一定都是程序,还有而是程序运行时读写的数据
2.3 文件名
文件名包括:文件路径—文件名主干—文件后缀
3.二进制文件和文本文件
根据文件的内容,可以分为
数据在内存中以二进制形式存储,如果不加转换的直接输出到外存文件中,就是二进制文件
如果要求在外存中以ASCII码值存储,则需要在存储前转换,以ASCII字符存储的文件就是文本文件
字符一律以ASCII码值存储 ,数值型数据既可以用ASCII码值存放,也可以使用二进制形式存储
以10000为例,如果以ASCII码值存储,则占用5个字节(每个字符一个字节),以二进制存储,就是4个字节
4.文件的打开和关闭
4.1 流和标准流
4.1.1 流
程序数据需要输出到各种外部设备,也需要从外部设备读取数据,不同外部设备的输入输出操作不同,我们抽象出了流的概念
4.1.2 标准流
为什么我们在键盘上输入数据,向屏幕输出数据并没有打开流呢?那是因为,C语言程序在启动时,默认打开了三个流,我们使用scanf、printf就可以直接输入输出了。
-
stdin:标准输入流,在大多数的环境中是从键盘上输入的,scanf就是从标准输入流中读取数据
-
stdout:标准输出流,大多数环境中输出到显示器界面,printf就是将信息输出到标准输出流中
-
stderr:标准错误流,大多数环境中输出到显示器界面
这三个流的类型都是:FILE*,通常称为文件指针,C语言中就是通过FILE*的文件指针维护各种流的
4.2 文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字、文件状态、文件当前的位置),这些信息保存在一个结构体变量中
4.3 文件的打开和关闭
在编写程序的时候,打开文件按的同时会返回一个FILE*的指针变量指向该文件,建立起来指针和文件的关系
//打开文件
FILE *fopen(const char* filename,const char* mode);//文件名和打开文件的模式
//fopen如果打开文件成功,返回文件信息区地址,打开失败,则会返回NULL
int fclose(FILE * stream);//关闭文件
int main()
{ //绝对路径
FILE *pf = fopen("C:UsersLenovoDesktop","r");//将文件位置写过来,并且将单斜杠()改为双斜杠(),原因是为了避免形成转义字符
//相对路径
//.表示当前路径
//..表示当前路径的上一级路径
if(pf==NULL)
{ perror("fopen");
return 1;
}
else printf("打开成功");
//关闭文件
fclose(pf);
pf=NULL;//避免成为野指针

以w形式打开文件时,如果没有文件存在,会新建一个文件,如果存在同名文件,会将同名文件的内容全部删除
5.文件的顺序读写
5.1 顺序读写函数介绍

-
fread/fwrite是二进制输入/输出
-
所有输入流指的是适用于标准输入流和其他输入流(如文件输入流),所有输出流适用于标准输出流和其他输出流(如文件输出流).
-
f系列的函数都是针对所有输入输出流
5.1.1字符读/写函数
fgetc如果读取成功,返回字符的ASCII码值,读取失败或遇到文件末尾,返回EOF,读取失败会设置一个错误的状态值,用ferror检测,遇到文件末尾,会设置一个遇到文件末尾的状态值
int fputc(int character,FILE*Pf);//写的字符和文件指针//写字符到pf指向文件中
int fgetc(FILE*pf);//返回读到的字符的ASCII码值//读取pf文件中的一个字符
//写字符到文件中
int main()
{ FILE*pf = fopen("文件名","w");//以写的形式打开
if(pf == NULL)
{ perror("fopen");
return 1;
}
//写文件
fputc('a',pf);//一次只能读取一个字符
//关闭文件
fclose(pf);
pf=NULL;
//读取字符
int main()
{ FILE*pf = fopen("文件名","w");//以读的形式打开
if(pf == NULL)
{ perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c
",ch);
//读取多个字符
int ch = 0;
while((ch = fgetc(pf))!=EOF)
{ printf("%c ",ch);};
//关闭文件
fclose(pf);
pf=NULL;
//关闭文件
5.1.2.字符串读写函数
fputs不会主动换行
int fputs(const char*str,FILE*stream);
char *fgets(char*str,int num,FILE *stream);//num是字符串中最多放的字符,并且会给 留一个位置//读到的字符拷贝到str中
int main()
{ FILE*pf = fopen("文件名","w");//以写的形式打开
if(pf == NULL)
{ perror("fopen");
return 1;
}
//写文件
fputs("hello world",pf);//这个代码写完会放到一行,如果要有换行的效果,则需要主动添加换行符
fputs("hello bit",pf);
//关闭文件
fclose(pf);
pf=NULL;
}
//从文件中读取一行数据
int main()
{ FILE*pf = fopen("文件名","r");
if(pf == NULL)
{ perror("fopen");
return 1;
}
//写文件
char arr[6];
fgets(arr,8,pf);//只能在pf中读取到7个,最后一个留给
//关闭文件
fclose(pf);
pf=NULL;
5.1.3.格式读/写函数
int fprintf(FILE*stream;const char*format……);
int fscanf(FILE*stream;const char*format……);
//向文件中写数据
int main()
{ FILE*pf = fopen("文件名","w");
if(pf == NULL)
{ perror("fopen");
return 1;
}
//写文件
char arr[20]="hello";
int num = 100;
double pi = 3.14;
fprintf(pf,"%s %d %lf",arr,num,pi);
//关闭文件
fclose(pf);
pf=NULL;
//读文件
struct S
{ char arr[20];
int num;
double pi;
};
//向文件中写数据
int main()
{ FILE*pf = fopen("文件名","r");
if(pf == NULL)
{ perror("fopen");
return 1;
}
struct S s={"world",100,3.14};
fprintf("pf","%s %d %f",s.arr,s.num,s.pi);
}
//关闭文件
fclose(pf);
pf=NULL;
//读文件
struct S
{ char arr[20];
int num;
double pi;
};
//向文件中写数据
int main()
{ FILE*pf = fopen("文件名","w");
if(pf == NULL)
{ perror("fopen");
return 1;
}
struct S s={0};
fscanf("pf","%s %d %f",s.arr,&(s.num),&(s.pi));
}
//关闭文件
fclose(pf);
pf=NULL;
5.1.4二进制输入输出
size_t fwrite(const void* ptr,size_t size,size_t count,FILE *stream);//写数据块到流文件中
// 字节大小 数据个数
size_t fread(const void* ptr,size_t size,size_t count,FILE *stream);
int main()
{
FILE *pf = fopen("文件名","wb");
if(pf == NULL)
{ perror("fopen");
return 1;
}
//写数据
int arr[]= {1,2,3,4,5};
fwrite(arr,sizeof(arr[0]),5,pf);
//读数据
int arr[10]= { 0 };
fread(arr,sizeof(int),5,pf);
for(int i = 0;i < 5;i++)
{ printf("%d",arr[i]);}
fclose(pf);
pf = NULL;
5.2 对比一组函数
scanf/fscanf/sscanf
printf/fprintf/sprintf
scanf(输入)/printf(输出):针对标准输入(stdin-键盘)/输出(stdout-屏幕)格式化的输入/输出函数
fscanf/fprintf:针对所有输入流/输出流的格式化的输入/输出函数
sscanf/sprintf:将字符串转换为格式化数据/把格式化的数据转换成字符串
int sprintf(char *str,const char* format……);//把格式化的数据转换成字符串
int sscanf(const char *s,const char *format……);//将字符串转换为格式化数据
struct S
{
char arr[20];
int i;
float pai;
}
int main()
{
struct S s = {“hello”,20,3.14};
char arr[30] = {0};
sprintf(arr,"%s %d %lf",s.arr,s.i,s.pai);
printf("%s
",arr);
}
6.文件的随机读写
6.1fseek——读写位置定位函数
根据文件指针的位置和偏移量来定位文件指针
int main()
{
FILE *pf = fopen("文件名","wb");
if(pf == NULL)
{ perror("fopen");
return 1;
}
int ch = fgetc(pf);
fputc(ch,stdout);
//fseek(pf,3,SEEK_SET);//定位文件指针指向‘d’
fseek(pf,-3,SEEK_END);
ch = fgetc(pf);
fputc(ch,stdout);
//光标指向的是e
long int r = ftell(pf);
printf("%ld
",r);
rewind(pf);//让文件指针回到起始位置
ch = fgetc(pf);
fputc(ch,stdout);
fclose(pf);
pf = NULL;
int fseek(FILE* stream,long int offset,int origin);
//偏移量
其中,origin有三个取值,SEEK_SET(文件开始的位置),SEEK_CUR(文件指针(文件内容中的光标)的当前位置),SEEK-END(文件结尾的位置)
6.2ftell——读写位置函数
返回文件指针相对于起始位置的偏移量
long int ftell(FILE *stream);
6.3rewind——文件头定位函数
让文件指针回到文件的起始位置
void rewind(FILE *stream);
7.文件读取结束的判定
7.1被错误使用的feof
- 在文件读取过程中,不能用feof的返回值来判断文件的读取是否结束
- 读取文件的过程中,结束有两个原因:1.遇到文件结尾2.读取遇到错误,失败
- feof的作用是:当我们发现文件读取已经结束了,看是不是因为遇到文件结尾而结束的,ferror函数用来判断是否读取遇到错误而结束的
-
文本文件读取是否结束,判断返回值是否为EOF(fgetc)或者NULL(fgets)
- fgetc:判断是否为EOF
- fgets:判断返回值是否为NULL
-
二进制文件读取是否结束,
- fread判断返回值是否小于实际要读的个数
8.文件缓冲区
ANSIC标准中采用“缓冲文件系统”处理数据文件的,所谓文件缓冲系统是指系统自动打在内存中为程序的每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘中输出数据会先送到内存的缓冲区,装满缓冲区后才一起送到磁盘上,如果从磁盘向计算机输入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个的将数据送到程序数据区。缓冲区的大小根据C编译系统决定。
完。
如有任何错误,欢迎各位指正,我们共同进步!







