C语言——文件操作
为什么使用文件
文件是存放在硬盘上的。当数据存放在内存中时,程序结束,数据就会丢失;
若想实现数据持久化,就要把数据存放在电脑硬盘上.
文件分类
程序文件:
源程序文件(例:后缀为.c)、目标文件(例:windows下后缀为.obj)、可执行程序.
数据文件:
我们写的源文件中的代码可能会涉及操作一个文件,比如向文件中写入内容,
该文件的内容是程序运行时读写的内容,该文件就是数据文件
例:
文件名
一个文件要有唯一的文件标识.
包含3部分:文件路径 + 文件名主干 + 文件后缀
例:C:\code\test.txt
(C盘里的一个文本文件)
文件信息区
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息
(文件名、文件状态等)
这些信息是保存在一个结构体变量中,该结构体类型是系统声明,取名FILE.
打开一个文件时,
系统会根据文件的情况自动创建一个FILE类型变量,并自动填充其中信息,不用关心细节.
文件指针/文件类型指针
一般通过FILE指针来维护这个结构体变量.
FILE* pa;//文件指针变量
通过文件指针可以找到某个文件的文件信息区,而文件信息区又强行与文件相关联,
所以可以通过文件指针进行文件操作.
文件的打开和关闭
要给瓶子灌水/放水,总要先打开瓶盖,最后拧上瓶盖,文件操作也是如此.
文件在读写之前应先打开文件,使用结束后应关闭文件.
ANSIC规定用fopen函数来打开文件,用fclose函数关闭文件.
fopen函数
原型:
FILE* fopen(const char* filename, const char* mode)
用法:
filename是文件名,mode是打开方式.
部分打开方式如下:
打开方式 | 含义 | 若文件不存在 |
---|---|---|
"r"(只读) | 打开一个已经存在的文本文件 | 出错 |
"w"(只写) | 打开一个文本文件,若文件存在则销毁文件中的内容 | 建立一个新文件 |
"a"(追加) | 向文本文件尾部添加数据 | 建立新文件 |
例:
FILE* pa = fopen("C:\\code\\test.txt", "r")
//两个\转义为一个\
若文件存在:fopen函数会在内存中创建与该文件C:\code\test.txt相关联的文件信息区,
把文件信息区填好,并返回该文件信息区的起始地址.
若文件不存在:打开失败,返回空指针NULL.
FILE* pa = fopen("C:\\code\\test.txt", "r");
if(NULL == pa)//判空操作
{
perror("fopen");
return 0;
}
else
…………………………
fclose函数
原型:
int fclose(FILE* stream)
用法:(接上面的代码)
fclose(pa);//关闭文件
pa = NULL; //pa及时置为空指针
若关闭成功,返回0.
文件内容的读写
流
我们电脑上有许多外部设备——例如键盘、屏幕、硬盘、网卡......
每种外部设备都有所差异,所以操作这些外部设备的方法不一样.
比如把数据打印到屏幕(把数据写到屏幕上)、从键盘中读取数据......
此时我们还要知道各种设备怎么读写.
为了降低学习成本,设计者在各种外部设备的上层封装了 流.
我们不用关心流怎么把数据放到对应的设备中,或者怎么从对应的设备中获取数据.
要读取信息时,从对应的流里读;要写信息时,把数据写到对应的流中。
流会自动找到对应的外部设备进行读写信息.
例如,我要打印信息到屏幕上——就会先把数据写到标准输出流中,标准输出流会把数据写到屏幕
注意:一个C程序运行起来,下面三个流是默认打开的,它们的类型竟然都是FILE*.
标准输入流stdin(关联键盘).
标准输出流stdout(关联屏幕).
标准错误流stderr(关联屏幕).
而文件流(关联硬盘中的文件)需要编程者自己打开,即文件指针。////
文件内容的顺序读写
按照一定的顺序进行读或写。
接下来先关注文件流
功能 | 函数 | 适用流 |
---|---|---|
字符输出 | fputc | 所有输出流 |
字符输入 | fgetc | 所有输入流 |
文本输出 | fputs | 所有输出流 |
文本输入 | fgets | 所有输入流 |
格式化输出 | fprintf | 所有输出流 |
格式化输入 | fscanf | 所有输入流 |
二进制输出 | fwrite | 文件流 |
二进制输入 | fread | 文件流 |
什么是输出?把程序的数据传输到外部。(把程序的数据写到流中,例如写文件,把信息写到文件中)
什么是输入?把外部数据传给程序。(从流中读取数据,例如读文件,读取文件信息到程序中)
fputc函数
原型:
int fputc(int c, FILE* stream)
功能:
写一个字符到 流 中.(例:可以把写一个字符到文件中)
使用:
//以写的方式打开文件
FILE* pa = fopen("D:\\cde.txt", "w");
if (pa == NULL)
{
perror("fopen");
return;
}
//写内容到文件中去
fputc('a', pa);//注意会按顺序写abc
fputc('b', pa);
fputc('c', pa);
//关闭文件
fclose(pa);
return 0;
//再次运行一次,会发现文件之前的内容丢失,因为以"w"方式打开,文件已存在会先销毁文件内容
FILE* pa = fopen("D:\\cde.txt", "w");
if (pa == NULL)
{
perror("fopen");
return;
}
for (int i = 'k'; i <= 'z'; i++)
{
fputc(i, pa);
}
flose(pa);
pa=NULL;
return 0;
fgetc函数
原型:
int fgetc(FILE* stream)
功能:
从流中读取一个字符,读取成功则返回字符的ANSIC码值.(可用来读文件)
若读取过程中 遇到一个错误 或者 读到文件末尾,返回EOF(end of file).
使用:
//接上面的文件内容——已经写进去'k'~'z'
FILE* pa = fopen("D:\\cde.txt", "r");//此时改为读文件
if (pa == NULL)
{
perror("fopen");
return;
}
int ch = 0;
while (( ch = fgetc(pa) ) != EOF)
{
//先把fgetc(pa)读到的字符的ASCIC码赋给ch, 再判断ch是否为EOF
//如果是EOF,循环停止
//如果不是,把读到的字符打印出来
printf("%c", ch);
}
运行效果:
fputs函数
原型:
int fputs(const char* string, FILE* stream)
功能:
string:要输出的字符串;stream:流.
把一个字符串输出到流中,可以直接把一个字符串写入到文件中(最终效果).
使用:
FILE* pf = fopen("D:\\cde.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 0;
}
fputs("shxmll", pf);//写一个字符串;
fputs("ysgz\n", pf);//再写一个字符串,后面加换行符
fputs("你好世界",pf);
fclose(pf);
运行效果:
fgets函数
原型:
char *fgets(char *string,int n, FILE *stream)
功能:
将从流中最多读到的(n-1)个字符放到string中。
string:(存放读取的字符串)的位置
n:最大读取的字符个数
stream:从哪个流中读取数据
返回值:读到的字符串的(存储首地址)
当遇到文件结束标志EOF或一个错误时,会返回NULL
注意:
1 实际上,fgets最多会从文件中读取(n-1)个字符,放到string中,并添加'\0'.
FILE* pf = fopen("D:\\cde.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 0;
}
char arr[20] = "xxxxxxxxxx";
fgets(arr, 4, pf);
fclose(pf);
2 fgets一次只读一行,但文本文档中第一行后还隐含一个换行字符,所以fgets也把它读进arr。
使用:
FILE* pf = fopen("D:\\cde.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 0;
}
char arr[50] = { 0 };
while (fgets(arr, 50, pf) != NULL)//用一次fgets后,下一次用就会读取文件的下一行
{
//只要不返回NULL,读取成功
printf("%s", arr);
}
fclose(pf);
运行效果
最后一行没放'\n'
fprintf函数
原型:
int fprintf(FILE *stream, const char *format[,argument]...)
功能:
可以将格式化的数据打印/写到所有输出流中。
类比printf, printf是将格式化的数据打印/写到屏幕中(标准输出流)
例:
typedef struct S//先声明一个学生结构体
{
char name[10];
int age;
double weight;
}S;
int main()
{
FILE* pf = fopen("text3.txt", "w");//在源文件路径下创建一个文件text3.txt
if (pf == NULL)
{
perror("fopen");
return 1;
}
S stu = { "xm", 20, 55.5 };
//把内容写到文件上
fprintf(pf, "%s %d %.2lf", stu.name, stu.age, stu.weight);
//把内容写到屏幕上
printf("%s %d %.2lf", stu.name, stu.age, stu.weight);
return 0;
}
运行效果:
注意:
fprintf的格式化输出之间没有空格,那么打印到文件中也是密密麻麻的。
fscanf函数
原型:
int fscanf(FILE *stream, const char *format[,argument]...)
功能://
可以从所有输入流中,读取格式化的数据。
类比scanf, scanf可以从键盘中读取格式化的数据。
例:
S stu = { 0 };//初始化学生结构体变量为0
//从之前的text3.txt中读取信息
fscanf(pf, "%s%d%lf", stu.name,&(stu.age), &(stu.weight));
//打印到屏幕上
fprintf(stdout, "%s %d %.2lf", stu.name, stu.age, stu.weight);
运行效果:
fwrite函数
原型:
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream)
功能:
将buffer这块空间的count个size字节大小的数据 以二进制的方式 写到流中
size_t size —— 一个数据的大小(单位byte)
size_t count —— 要写多少个数据
注意:
以二进制的方式写入,之后就要以二进制的方式读取,必须严格匹配.
使用:
typedef struct Student
{
char name[10];
int age;
double weight;
}Student;
int main()
{
//"wb"是以二进制的方式写文件,若文件存在也会先销毁文件内容
FILE* pf = fopen("text3.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
Student stu = { "veri", 35, 66.6 };
fwrite(&stu, sizeof(Student), 1, pf);
fclose(pf);
return 0;
}
用记事本打开文件后,会发现有很多内容很古怪,这是因为记事本不用来读取二进制内容。
但之后用二进制的方式读取文件也能读取到有效信息
fread函数
原型:
size_t fread(void *buffer, size_t size, size_t count, FILE *stream)
功能:
以二进制的方式从 流 读取count个size字节大小的数据放到buffer中。
返回值为成功读取的数据个数【所以size和count不能写反】
使用:
//"rb"是以二进制方式读取文件
FILE* pf = fopen("text3.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
Student stu = { 0 };
while( fread(&stu, sizeof(Student), 1, pf) == 1)//每次读取一个数据(每个数据大小是sizeof(Student)),读取成功返回1,失败返回0
printf("%s %d %.2lf", stu.name, stu.age, stu.weight);
fclose(pf);
运行效果:
打印出之前二进制写入的内容
文件内容的随机读写
以上的函数都默认从文件的第一行开始有序读写数据。
文件内部指针
在操作一个文件进行读写时,会有一个内部指针指向下一个将要读写的位置。
默认在打开文件时,该文件内部指针是指向第一行第一列的。
每进行一次读写时,该内部指针会自动移动,指向下一次读写的位置。
如果能改变文件内部指针的位置,就能实现读写任意地方的内容
fseek函数
原型:
int fseek(FILE* stream, long offset, int origin)
功能:
概括:移动文件内部指针到特定的位置
origin 有3个值
1、 SEEK_CUR 文件内部指针当前位置
2、 SEEK_END 文件末尾
3、 SEEK_SET 文件开始位置
offset —— 偏移量。若为正,代表向右偏移;为负,向左偏移
例:
//pf和pa是2个不同的文件指针
feek(pf, 3, SEEK_CUR);//将文件内部指针向右移3个单位
feek(pa, -2, SEEK_END);//将文件内部指针移到文件末尾向左2个单位的位置
注意:
fseek只是把文件内部指针移到指定的位置,并不会进行读写;
但如果以追加方式打开文件,feek不起作用。
使用:
要读取的文件
FILE* pf = fopen("D:\\cde.txt", "r");
char re1 = fgetc(pf);
char re2 = fgetc(pf);
fseek(pf, 3, SEEK_CUR);//使文件内部指针向右偏离3个字符
char re3 = fgetc(pf);
printf("%c%c%c", re1, re2, re3);
输出效果
拓展:
文件末尾位置:可以理解为 【没有任何数据可以读写】的开始位置
FILE* pf = fopen("D:\\cde.txt", "w");
fputc('a', pf);
fseek(pf, 3, SEEK_CUR);//使文件内部指针向右偏离3个字符
fputc('c', pf);
fseek(pf, 3, SEEK_END);//使文件内部指针移到文件结束的后面3个字符
fputc('k', pf);
//注意:文件里并不存在EOF,这只是一种状态,在没有可读取的数据时,还想读取数据,
//就会返回EOF,但写文件就可以在没有数据的地方写
fclose(pf);
程序运行后文件情况:
rewind函数
原型:
void rewind(FILE* stream)
功能:
将文件内部指针移动到文件开始位置。
实际上也能用 fseek(pf, 0, SEEK_SET)代替,但rewind(pf)更简便.
ftell函数
原型:
long ftell(FILE* stream)
功能:
返回文件内部指针当前位置离文件开始位置的偏移量.
文件读取完成的判断
feof函数
原型:
int feof(FILE* stream)
功能:
判断当前文件内部指针是否指向文件末尾。是:返回非0值;否:返回0。
通常用来判断读取文件完成后,是读到文件末尾结束,还是遇到了错误提前结束。
例:
FILE* pf = fopen("D:\\cde.txt", "r");
char w = 0;
while ( ( w=fgetc(pf) ) != EOF)
{
printf("%c\n", w);
}
if (feof(pf) == 0)
{
printf("读取文件过程中遇到错误!!!\n");
}
else
{
printf("正常读取文件\n");
}
fclose(pf);
怎么判断文件全部数据读取完成?
1、文本文件读取结束:
fgetc的返回值是EOF,fgets的返回值是NULL
2、二进制文件读取结束:
fread的返回值小于读取的数据个数。
//一般用循环一个数据一个数据来读,读到返回1;没有读到,就读取结束返回0
while(fread(&stu, sizeof(Student), 1, pf) == 1)
{
printf(".....");
}
文件缓冲区
系统自动为正在使用的文件开辟一块文件缓冲区【不同于文件信息区】
从内存向硬盘中的文件输出数据时(写文件时),会把数据先写到文件缓冲区中,
装满缓冲区后(或刷新缓冲区)才会输出到文件中;
读取文件时,会把读到的数据先放到缓冲区中,
装满缓冲区后(或刷新缓冲区)才会把数据逐个给对应的程序变量
但输入和输出的缓冲区不一样
//证明缓冲区的存在
FILE* pf = fopen("D:\\cde.txt", "w");
fputs("abcde", pf);
Sleep(10000);//程序睡眠10秒,在这10秒的时间里打开文件,会发现文件并没有写入数据
printf("缓冲区刷新完成\n");//此时再打开文件,文件才会有数据
fclose(pf);//fclose也会刷新文件缓冲区
为什么要及时关闭文件?fclose会刷新文件缓冲区,防止有数据在内存的缓冲区中,导致数据丢失。
当然也能用fflush(pf)手动刷新文件缓冲区。
上一篇: C语言中的文件操作及其函数应用介绍
下一篇: C语言 编写“剪刀石头布”小游戏
推荐阅读
-
C语言内存管理的重要性
-
使用Java进行文件压缩和解压缩操作
-
【华为云】文件夹解压操作
-
使用opencv3和C++编程语言实现简单目标跟踪的Tracker
-
在线代码执行工具(支持PHP,Java,C++等语言)-访问 http://www.it1352.com/Onlinetools
-
《c语言程序设计a》在线作业 - 16秋华师华师c语言作业
-
《高级语言程序设计A》在线作业二:西南交通大学16秋C语言网上作业
-
18春《c语言》在线作业3,华师18春《C语言程序设计A》在线作业参考
-
c语言程序设计作业A,华师19春《C语言程序设计A》在线作业
-
实现纯前端下载PDF链接文件的解决方案,不进行预览操作