🏳🌈前言
对一台计算机来说,最基本的功能即存储数据;一般情况下,数据在计算机中上都是以文件形式存储的。在C语言程序设计中是需要对文件进行操作的。比如:打开文件、读写文件、关闭文件等操作;本章就对文件操作进行详细讲解。
1. 文件概念
文件:存储在外部介质上数据的集合。外部介质比如说磁盘(硬盘)。
在存储文件的时候必须写文件名,通过文件名来访问所保存的文件。一个文件要有唯一的标识符,方便用户查找。文件标识包括3部分:
①文件路径;②文件名;③文件扩展名;【注意的是:文件名中不能有特殊符号,比如说‘/’,因为存在转移字符和文件路径冲突】
然而:文件扩展名有很多,比如:.txt、doc、jpg、c、exe等。
2. 文件分类
根据数据的组织形式,文件可以分为文本文件、二进制文件。
2.1 文本文件
文本文件:又被称为ASCII文件。每一个字节存放一个字符的ACSCII码。比如:整数10000。若用文件形式输出到磁盘上,存储形式如下图:
从上图可以看出,一个字符占用一个字节,整数10000共占5Byte。
2.2 二进制文件
数据在内存中是以二进制形式存储的,若不加转换地输出到磁盘上去,就是二进制文件。简单说,二进制文件就是存储在内存中的数据的映像,所以叫做映像文件。比如:整数10000。若以二进制文件存储到磁盘中:
可以看到只需要4个字节,只是存储内容不直观,需要转换一下才可以一眼看出。
2.3 文本文件和二进制文件的对比
(1)文本文件
优点:①文本文件比较直观,便于对字符进行逐个处理。②不需要任何转换。
缺点:①占用的存储空间较多。②读取文件时候花费更多时间。(因为需要转换一次)
(2)二进制文件
优点:①节省空间。②节省时间。
缺点:①存放内容不直观。
总结:若希望加载文件和生成文件的速度较快,并且生成的文件较小,建议使用二进制文件;若希望生成的文件无需经过任何转换就可以看到存放内容,建议使用文本文件。
3、文件指针
在C语言中,文件的所有操作必须依赖文件指来完成。在进行文件操作的时候,必须先将文件和文件指针联系起来。
3.1文件指针定义形式
FILE* <变量名>;
上面的
FILE
是由系统声明的定义文件指针的结构体,用于保存文件相关的信息,比如:文件名、文件位置、文件大小、文件状态等信息。下面是C语言中FILE结构体定义 :
typedef struct
{
short level;//缓冲区满或空的程度
unsigned flags;//文件状态标识
char fd;//文件描述符
unsigned char hold;//若无缓冲区不读取字符。
short bsize;//缓冲区大小
unsigned char* buffer;//数据传送缓冲区位置
unsigned char* curp;//当前读写位置
short token;//无效检测
}FLIE;//结构体类型名
每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信息,使⽤者不必关⼼细节。
- 有文件结构体之后,就可以文件指针了,形式如下:
FILE* pf;//文件指针
定义好文件指针后,还未和文件关联起来,此时文件信息区还没有任何文件信息。
文件指针需要通过fopen()
函数来关联文件,它用于打开文件。如下:
FILE* pf=fopen("test.txt");//打开文件test。
4. 文件操作
4.1 文件打开\关闭
文件最基本的操作就是文件的打开和关闭。在对文件进行任何读写的前提都是先打开文件,读写完后再关闭文件。
4.1.1 fopen()函数
文件操作之前,第一步先打开文件,函数原型如下:
FILE* fopen(const char* filename,const char* mode);
返回类型为FILE* 表示文件指针类型;
filename
用于指定文件的绝对路径,即包含路径名和文件的扩展名。
mode
表示文件的打开模式。
文件打开模式如下:
打开模式 | 名称 | 作用 |
---|---|---|
“r”/“rb” | 只读模式 | 打开一个文本文件/二进制文件,只允许读取数据,文件不存在时打开失败,返回NULL。 |
“w”/“wb” | 只写模式 | 创建一个文本文件/二进制文件,只允许写入数据,文件已经存在的时候,则覆盖原来文件;不存在时会创建新的文件。 |
"a”/“ab” | 追加模式 | 打开一个文本文件/二进制文件,只允许在文件末尾添加数据,文件不存在时,会创建新的文件。 |
"r+”/“rb+” | 读取/更新模式 | 打开一个文本文件/二进制文件,允许进行读取和写入操作,文件不存在时打开失败,返回NULL。 |
“w+/“wb+” | 写入/更新模式 | 创建一个文本文件/二进制文件。允许进行读取和写入数据,若文件已经存在,则重写文件,没得就创建一个文件。 |
“a+”/“ab+” | 追加/更新模式 | 打开一个文本文件/二进制文件,允许进行数据读取和追加操作,文件不存在,则创建新文件。 |
4.1.2 fclose()函数
打开文件后,读写结束后,需要关闭文件。关闭文件是释放缓冲区和其他资源的过程,不关闭会耗费系统资源。fclose()函数就是用来关闭文件的。声明如下:
int fclose(FILE* pf);//返回值为int,关闭成功返回0,否则返回EOF(-1)。
4.2 文件操作实例
//1、文件操作
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf=fopen("hello.txt","w");//打开文件,不存在则创建
//判断
if(pf==NULL)
{
perror("open file fail");
exit(0);//退出程序
}
//写入一个字符串
fputs("Hello word!\n",pf);
//关闭文件
fclose(pf);
printf("文件写入成功!\n");
return 0;
}
结果展示:
4.3文件写入函数
在程序设计时,经常会对文件进行读写操作。文件的读写操作分为两种形式:①以字符形式进行写入;②以二进制形式进行写入;
4.3.1 fputc()函数——单个字符写入
fputc()
函数作用:向文件中写入一个字符,只能单个字符写入。声明如下:
int fputc(int c,FILE* stream);
c
表示写入的内容,
stream
表示文件指针。
函数返回值为int
,
写入成功,返回成功写入的字符,否则返回EOF
。
1、代码演示:
//2、fputc()函数使用,向文件写入一个字符
#include<stdio.h>
#include<stdlib.h>//exit()函数的头文件
int main()
{
//写入的内容
char ch[]="I like program!";
FILE* pf=fopen("hello.txt","w");
//判断
if(pf==NULL)
{
printf("打开文件失败!\n");
exit(0);
}
//写入
int i=0;
while(ch[i]!='\0')
{
fputc(ch[i],pf);
i++;
}
//关闭文件、
fclose(pf);
return 0;
}
结果展示:
4.3.2 fputs()函数——字符串写入
fputs()
函数的作用:将字符串写入文件中,声明如下:
int fputs(const char* str,FILE* stream);
str
表示:带写入的字符串的字符指针。
stream
表示文件指针。
写入成功函数返回0
,否则返回EOF
。
2、代码演示:
//3、fputs()函数的使用,写入字符串
#include<stdio.h>
#include<stdlib.h>
int main()
{
char *str[3];//定义字符数组的指针——字符数组指针
str[0]="I like program!\n";
str[1]="It is amazing!\n";
str[2]="It is interesting!\n";
FILE* pf=fopen("hello.txt","w");
//判断
if(pf==NULL)
{
printf("文件打开失败!\n");
exit(0);
}
//写入
for(int i=0;i<3;i++)
{
fputs(str[i],pf);
}
//关闭
fclose(pf);
return 0;
}
结果展示:
4.3.3 fwrite()函数——二进制写入
fwrite()
函数的作用:以二进制的形式写入文件。声明如下:
size_t fwrite(const void* ptr,size_t size,size_t number,FILE* stream);
ptr
表示:待写入数据的指针;
size
表示:写入数据的字节数;
number
表示:待写入size字节的数据的个数;
stream
表示:文件指针;
返回值为size-t
,即无符号整型,写入成功返回写入的数据次数,否则返回0;
3、代码演示:
//4、fwrite()函数的使用,二进制形式写入文件
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[26];//存放26英文字符
FILE* pf=fopen("hello.txt","w");
if(pf==NULL)
{
printf("打开文件失败!\n");
exit(0);
}
for(int i=0;i<26;i++)
{
str[i]='a'+i;
}
int num=fwrite(str,sizeof(char)*13,2,pf);//第一次写入前13个字母,
//第二次写入后面13个
printf("写入次数:%d\n",num);
fclose(pf);
return 0;
}
结果展示:
文件中内容:
4.3.4 fprintf()函数——格式化写入
fprintf()
函数的作用:将数据格式化后写入文件。声明如下:
int fprintf(FILE* stream,const char* format,……);
stream
表示:文件指针;
format
表示:以什么样的字符串格式输出到文件中;
该函数根据指定的字符串格式将字符串写入到指定文件。
调用成功,返回输出的字符数
,否则返回EOF
。
4、代码演示:
//fprintf()函数的使用,格式化写入
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf=fopen("hello.txt","w");
if(pf==NULL)
{
printf("打开失败!\n");
exit(0);
}
//将格式化的字符串输出到文件中
fprintf(pf,"I am a %s,I am %d years old.","programer",26);
fclose(pf);
return 0;
}
结果展示:
4.4 文件读取函数
在C语言中,除开写入函数外,还有文件读取函数。下面一一介绍文件读取函数:
4.4.1 fgetc()函数——单个字符读取
fgets()
函数的作用:读取文件的字符,单个读取。声明如下:
int fgetc(FILE* stream);
stream
表示:文件指针。
函数将读取的字符转换为整数返回,读取文件到末尾或读取错误返回EOF
。
1、代码演示:
//6、fgetc()函数的使用,读取单个字符
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf=fopen("hello.txt","r");
if(pf==NULL)
{
printf("打开文件失败!\n");
exit(0);
}
//读取
char ch=0;
while((ch=fgetc(pf))!=EOF)
{
printf("%c",ch);
}
fclose(pf);
return 0;
}
结果展示:
4.4.2 fgets()函数——字符串读取
fgets()
函数的作用:从文件中读取一行字符串,或则读取指定长度的字符串,声明如下:
char* fgets(char* s,int size,FILE* stream);
s
表示:用来存储数据的空间;
size
表示:读取的数据大小;
stream
表示:读取文件的文件指针;
读取成功返回s
,读取错误或则遇到文件末尾,则返回NULL。
fgets()从文件中读取数据时候,最多读取size-1个数据,将读取的数据保存到s指向的字符数组中,读取的字符串会在最后添加一个’\0’。
【fgets()函数读取停止的情况一般是这3种:】
①读取size-1
个字符前,遇到'\n'
,读取结束,末尾添加'\0'
。
②读取size-1
个字符前,遇到EOF
,读取结束,末尾添加'\0'
。
③完成size-1
个字符读取,读取结束,末尾添加'\0'
。
2、代码演示:
//7、fgets()函数的使用,读取字符串
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
FILE* pf=fopen("hello.txt","r");
if(pf==NULL)
{
printf("打开文件失败!\n");
exit(0);
}
//读取
char str[50];//文件数据缓冲区
while(!feof(pf))//判断文件指针是否已经指到文件的末尾。
{
fgets(str,sizeof(str),pf);//将文件中的数据复制到文件数据缓冲区str中
printf("%s",str);
}
fclose(pf);
return 0;
}
结果展示:
4.4.3 fread()函数——二进制形式读取
fread()
函数的作用:以二进制形式读取文件,声明如下:
size_t fread(void* ptr,size_t size,size_t number,FILE* stream);
ptr
表示:指向要接收读取数据的内存空间的指针;
size
表示:读取元素的大小;
number
表示:读取元素的个数;
stream
表示:文件指针。
fread()函数从一个文件中读取数据,最多读取number
个元素,每个元素的大小为size
个字节。读取成功,返回读取数据的大小,读取错误返回0
。
3、代码演示:
//fread()函数——二进制形式读取
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
FILE* pf=fopen("hello.txt","r");
if(pf==NULL)
{
printf("打开文件失败!\n");
exit(0);
}
char str[30];
memset(str,0,sizeof(0));//把字符数组初始化为0
int len=0;
while(!feof(pf))
{
len=fread(str,1,28,pf);
printf("%s\n",str);
}
fclose(pf);
printf("读取的实际大小:%d\n",len);
return 0;
}
结果展示:
4.4.4 fscanf()函数——格式化读取
fscanf()
函数的作用:从文件中格式化读取数据,声明如下:
int fscanf(FILE* stream,const char* format,……);
format
表示:文件中的数据以什么格式输入到程序中。
调用成功,返回输入的参数的个数;否则返回EOF
。
4、代码演示:
//9、fscanf()函数——格式化读取
#include<stdio.h>
#include<stdlib.h>
int main()
{
char arr1[100],arr2[100];
FILE* pf=fopen("hello.txt","r");
if(pf==NULL)
{
perror("fopen");
exit(0);
}
fscanf(pf,"%s %s",arr1,arr2);
printf("%s\n",arr1);
printf("%s\n",arr2);
fclose(pf);
return 0;
}
结果展示:
5. 文件随机访问函数
文件位置指针:简单说就是文件里面的光标
函数名 | 格式 | 作用 |
---|---|---|
rewind() | void rewind(FILE* stream); | 将文件位置指针指向文件开头 |
fseek() | int fseek(FILE* stream,long offser,int where); | 将文件位置指针指向指定位置。 |
ftell() | long ftell(FILE* stream); | 获取文件位置指针的当前位置 |
在这3个函数中,
fseek()
中的参数需要说明:
offset
:表示参数where
移动读/写位置的偏移量。
where
:有3个值可以取;
①SEEK_SET: 表示数字0
,表示从文件开头进行偏移。
②SEEK_CUR:表示数字1
,表示从文件位置指针的当前位置开始偏移。
③SEEK_END:表示数字2,表示从文件末尾开始偏移,此时向前偏移,所以offset
是负数。
偏移成功,返回0,失败返回-1。
6. 文件重命名和删除
函数名 | 格式 | 作用 |
---|---|---|
rename() | int rename(const char* oldname,const char* newname); | 对文件重命名 |
remove() | int remove(const char* Filename); | 删除文件 |
7.文件检测函数
函数名 | 格式 | 作用 |
---|---|---|
perror() | void perror(const char* str); | 打印错误信息 |
ferror() | int ferror(FILE* stream); | 检查输入/输出函数进行读/写操作时候是否报错 |
feof() | int feof(FILE* stream); | 判断文件是否处于文件结束位置 |
clearerr() | void clearerr(FILE* stream); | 清楚错误标志和文件结束标志 |