C语言进阶——文件操作

本文已收录至:C语言——梦想系列

更多知识尽在此专栏中!

C语言进阶——文件操作_第1张图片

欢迎点赞收藏关注


目录

前言

正文

关于文件

什么是文件?

文件有什么用?

文件的格式是什么?

使用文件

文件指针

文件的打开和关闭

顺序读写

输入、输出流

fputc 与 fgetc

fputs 与 fgets

fprintf 与 fscanf

fwrite 与 fread

随机读写

fseek

ftell

rewind 

fseek、ftell、rewind 三合一

文本文件与二进制文件

文本文件

二进制文件

注意 

文件使用注意事项

被错误使用的feof

文件读取结束原因判断

文件缓冲区

总结


前言

  文件——是我们生活中必不可缺的一部分,优秀的文件管理能使我们工作效率更高,比如上学时的点名册、平时记账的手账本、电脑中存储数据的各种文件夹等。数据构成文件,文件成就数据,因此我们需要学习C语言中的各种文件操作,使数据能够做到持久化存储。

C语言进阶——文件操作_第2张图片 图片来源:百度图片

正文

  文件操作涉及到的内容还是比较多的,大致可分为三个问题:是什么? 怎么用? 要注意什么? 从这三个问题可以衍生出很多问题,其中怎么用是内容最丰富的版块,让我们直接进入正题吧。

关于文件

什么是文件?

C语言进阶——文件操作_第3张图片

  如上图所示,这就是文件,不过这是传统的纸质文件。在我们电脑中的都是电子文件夹,存储的都是电子文件,比如数字、图片、文档等,这些数据都是存储在我们电脑的硬盘上的,只要硬盘没坏,那么这些数据随时随地都能找到,而且方便检索。在程序设计中,我们一般将文件分为两种:程序文件数据文件(从文件功能的角度分类),本文主要介绍的是数据文件。

程序文件

包括源程序文件,比如我们的 .c 文件;目标文件,经过预编译、编译、汇编后生成的目标文件,后缀为 .o ,对其进行链接后,就能生成可执行程序;当然最后一种就是可执行程序文件,后缀为.exe

数据文件

就像上图一样,主要存储的是各种数据信息,数据文件的职能是能让程序读取到数据,以及能够对其写入数据,这些数据是能够持久化存储的。

文件有什么用?

C语言进阶——文件操作_第4张图片 电脑C盘中存储的各种信息

  文件可以保存数据,使数据能做到持久化存储。文件可以使我们的操作更为合理,比如现在写的这篇博客,本质就是一个文件,不过是存储在服务器上的文件(数据)。电子文件的最大特点就是易于检索了,这也正是电脑的优点之一。至于C语言中的文件可以用于保存程序运行所产生的数据,比如通讯录系统,可以将联系人信息保存到文件中,现在的程序设计数据一般都是存储在数据库中,毕竟本地文件夹安全性还是比较低。

文件的格式是什么?

  所有文件都有唯一的标识符,标识符可以分为三部分:文件路径+文件名主干+文件后缀,比如存储在我电脑中的VS文件标识符为:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE

C语言进阶——文件操作_第5张图片

  为了方便称呼,我们一般将其称为文件名,比如 devenv.exe 就是一个典型的程序文件

使用文件

文件指针

  文件是一个庞大的集合体,类似于结构体,不过更为复杂,因此在C语言中有一个专门的指针 文件类型指针,简称为 文件指针 用来指向文件首地址。系统会将文件规范化,当使用文件时,系统会在内存中开辟一个对应的文件信息区,这个信息区中包括了文件的各种信息(文件名、文件状态、文件位置等),如果对应信息缺失,系统会自动补齐。前面说过,文件类似于结构体,因此整个文件信息是保存在一个庞大的结构体中的,为了与传统结构体区分开,专门创建了 FILE* 这种特殊的指针,即文件指针。

C语言进阶——文件操作_第6张图片

  因为VS2019将其分的太细了,这里不好演示,但知道 FILE 这个东西本质是个结构体就行了 

文件的打开和关闭

  文件得先打开,才能关闭,最好跟动态内存管理一样,有申请就要有释放,成对出现更为安全。先来说说打开吧!

文件打开

  文件打开用的是 fopen 这个函数,fopen 的作用是从一个文件中以某种方式打开文件,返回类型是 FILE* 即打开文件的起始地址,因此我们需要用一个 FILE* 类型的指针来接收。

C语言进阶——文件操作_第7张图片

注意:文件打开后,要对文件指针进行判断,如果指针为空,说明文件打开失败,此时要报错,并终止后续操作 

if (NULL == fp)
{
	//报错函数,说明此文件打开失败
	perror("fopen::test.txt!");
	return 1;//错误结束
}

目标文件

  有两种形式,一种是绝对地址,另一种是相对地址

绝对地址

即唯一路径,使用绝对地址访问文件时,文件可以在电脑中的任意位置,前提是地址要合法。绝对位置的文件标识符必须全,即文件路径+文件名主干+文件名后缀

比如 C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE

注意:

使用绝对路径时,需要在每个 \ 前额外加一个 \  原因很简单,单个 \ 与其他字符结合,可能会变成一个转义字符,因此需要两个 \\ 来表示一个 \  

如果是 Mac 就不需要担心这个问题,因为它用的是 / 

//绝对,指此地址是唯一的,能通过这个地址找到唯一的文件
FILE* fp = fopen(" C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\Common7\\IDE", "w");

相对位置

此时的路径是固定的,一般和当前源文件处于同一位置,相对嘛,就是相对于当前程序文件。相对位置只需要文件名主干+文件后缀就行了 。

比如 devenv.exe ,此时存储位置相对于上面的绝对地址,位于同一目录下

//相对,指在当前工程文件内的文件
FILE* fp = fopen("test.txt", "w");

打开方式 

  文件打开方式有很多种,比如只读、只写、读+、二进制写等……

  值得注意的是当我们通过的方式打开文件时,如果目标文件不存在,那么打开就会失败;但如果是通过的方式打开文件时,如果文件不存在,会自动创建一个目标文件

  下面来演示下用写的方式打开文件,然后文件不存在,自动创建文件的情况:

注意:这种是文件的标准使用方式,即先打开,然后判断是否打开失败,如果失败就报错,否则就可以使用文件,最后再关闭文件

//文件创建,通过程序创建
int main()
{
	//可以利用只读的特性:没有就会自己创建
	//这里是相对路径
	FILE* fp = fopen("test.txt", "w");//打开
	if (NULL == fp)
	{
		//报错函数,说明此文件打开失败
		perror("fopen::test.txt!");
		return 1;//错误结束
	}

	//进行文件操作……//

	fclose(fp);//关闭
	fp = NULL;//置空
	return 0;
}

C语言进阶——文件操作_第8张图片

C语言进阶——文件操作_第9张图片

注意:

  • 这个特点很好用,但也很致命,因为每次写文件,都相当于在覆盖文件,假如我们想对原文进行追加,就需要创建原来的数据,再创建新数据,然后一起写入文件中。其实面对这种场景,C语言还提供了另一种文件打开方式 追加 "a" ,下面就是各种打开指令的集合表。
文件打开指令 含义 如果目标文件不存在
"r"        只读 打开一个文件,只能对这个文件进行读取操作 打开失败,报错
"w"        只写 打开一个文件,只能对这个文件进行写入操作 建立目标文件
"a"        追加 向文件末尾处添加数据 建立目标文件
"rb"        只读 打开二进制文件,只能对其进行二进制读取 打开失败,报错
"wb"        只写 打开二进制文件,只能对其进行二进制写入 建立目标文件
"ab"        追加 向二进制文件末尾处添加数据 追加失败,报错
"r+"        读写 打开一个文件,可以进行读取和写入操作 打开失败,报错
"w+"        读写 打开一个文件,可以进行读取和写入操作 建立目标文件
"a+"        读写 对文件末尾处进行读取和写入操作 建立目标文件
"rb+"        读写 打开二进制文件,可以进行二进制读取和写入 打开失败,报错
"wb+"        读写 打开二进制文件,可以进行二进制读取和写入 建立目标文件
"ab+"        读写 对二进制文件末尾处进行读取和写入操作 建立目标文件

文件关闭

  文件关闭用到的是 fclose 这个函数,fclose 就比较简单了,只需要一个参数—文件指针(FILE*),然后就能关闭这个文件指针所指向的文件,感觉有点像 free,功能强大,使用方便。同 free 一样,fclose 关闭文件后,也需要将指针(文件指针)置空,避免出现野指针

C语言进阶——文件操作_第10张图片

fclose(fp);//关闭
fp = NULL;//置空

顺序读写

输入、输出流

  在介绍文件读写操作前,需要先说明一下C语言中“流”(format)的概念。假设将数据看作水流,那么它就有两个关键部分:从哪里流出(源头)、流入哪里(终点),其中流出可以看作输出流入可以看作输入。C语言中有三种流:标准输入输出流、文件输入输出流、二进制输入输出流(实际使用时用前两种流,第三种的目标流一般为文件)。

C语言进阶——文件操作_第11张图片

标准输入输出流

  标准输入输出流(I/O)包括标准输入流(stdin)—从键盘输入标准输出流(stdout)—从屏幕输出标准错误流(strerr)—从屏幕输出,任何一个C程序,只要运行起来都会默认打开以上三个流,比如我们常用的 scanf、printf就是基于标准输入输出流而运行的,这也正是二者需要键盘、屏幕的原因。

C语言进阶——文件操作_第12张图片

文件输入输出流

  顾名思义,文件输入输出流所依赖的载体为文件,无论是输入还是输出数据,都是在文件上进行的,因此它的对象类型为 FILE* ,文件输入输出流可以使用所有输入输出流函数,比如fputc、fprintf、fscanf等,使用时只需要加上目标流类型就行了。

C语言进阶——文件操作_第13张图片

二进制输入输出流

  二进制输入输出流主要适用于文件操作,对文件进行二进制数据的读取和写入,所以二进制输入输出一般用在文件操作中。二进制只有0、1这两个数,因此如果我们使用二进制输出流对某个文件进行写入,文件中存储的信息就变成了一串二进制数(可以使用二进制文件查看器观察),如果用普通文本的形式查看此文件,会得到一串乱码。二进制输入输出流有fwrite、fread这两个函数。

C语言进阶——文件操作_第14张图片

注意:

  • printf、scanf、gets等这种不需要指定目标流的函数,设计时就已经规定好了,它们是标准输入输出流函数。而fprintf、fscanf、fgets等这些面向所有输入输出流的函数更为原始,需要用户使用时根据具体情况选择目标流,所以这些函数也能实现标准输入输出流函数的功能,只需要把目标流写成 stdin(输入)、stdout(输出)就行了。下面是各种输入输出函数的集合表:
功能 函数名 适用于(目标流)
进行单字符的输入 fgetc                  所有输入流                
进行单字符的输出 fputc 所有输出流
文本行输入函数(读取一行数据) fgets 所有输入流
文本行输出函数(写入一行数据) fputs 所有输出流
格式化输入函数 fscanf 所有输入流
格式化输出函数 fprintf 所有输出流
二进制输入函数 fread 文件输入流
二进制输出函数 fwrite 文件输出流

注意:为了方便函数的介绍,接下来会先介绍写入(输出),再介绍读取(输入)函数

fputc 与 fgetc

  fputc 对文件进行单字符的写入fgetc 读取文件中的单字符

fputc

C语言进阶——文件操作_第15张图片

//文件读写之逐字符写
//因为没有数据,所以我们先写再读
int main()
{
	//打开
	FILE* fp = fopen("test.txt", "w");
	if (NULL == fp)
	{
		perror("fopen::test.txt!");
		return 1;
	}

	//进行操作
	char* pc = "abcdef123";
	//逐字符写入
	while (*pc)
	{
		fputc(*pc, fp);//逐字符放
		pc++;
	}

	//关闭
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第16张图片

fgetc 

C语言进阶——文件操作_第17张图片

//文件读写之逐字符读
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (NULL == fp)
	{
		perror("fopen::test.txt!");
		return 1;
	}
	//逐字符读取
	int ch = 0;//需要用整型,因为EOF是-1
	while ((ch = fgetc(fp)) != EOF)
	{
		//逐字符读取后,赋给字符变量ch,然后打印
		printf("%c", ch);
	}

	//关闭
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第18张图片

注意:

  • 在读取或写入字符串时,可以通过特定的条件结束读写。比如写入:可以通过字符串自带的结束标志 \0 结束写入读取:可以通过fgetc的返回值进行判读,如果返回 -1(EOF) 就说明数据已经读取完了
  • 单纯写文本数据时,要使用指令 "w" 单纯读数据时,要使用指令 "r" 指令与操作一定要匹配上,不然就会发生意想不到的错误

fputs 与 fgets

  fputs 对文件进行一行数据的写入fgets读取文件中的一行数据

fputs

C语言进阶——文件操作_第19张图片

//文件读写之行写
int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}

	char* pc = "这是由标准输入输出流写入的数据";
    fputs(pc, fp);//行写入
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第20张图片

fgets 

C语言进阶——文件操作_第21张图片

//文件读写之行读
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}

	char tmp[30] = "0";
	fgets(tmp, sizeof(tmp), fp);//行读取
	printf("%s\n", tmp);
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第22张图片

注意:

  • 行写入时,要确保写入的是字符串数据,传参数时要传地址;行读取时,需要设定待读取数据的数量,一般是跟待存储空间大小相匹配。如果行读取结束,有两种情况:1、因无法读取数据而结束   2、因读取到文件末尾而结束 
  • 单纯文本数据时,要使用指令 "w" ;单纯数据时,要使用指令 "r" 

fprintf 与 fscanf

  fprintf 是对文本进行格式化数据的写入fscanf 是将文本中的数据进行格式化读取

fprintf

C语言进阶——文件操作_第23张图片

//按照文件流格式化写入
struct S
{
	char name[20];
	int age;
	float score;
}a = { "张三",20,88.8f };
int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}
	fprintf(fp, "%s %d %.2f", a.name, a.age, a.score);//适用于所有流的格式化输入输出函数

	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第24张图片

fscanf

C语言进阶——文件操作_第25张图片

//按照文件流格式化读取
struct S
{
	char name[20];
	int age;
	float score;
}tmp;
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}
    fscanf(fp, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));//适用于所有流的格式化输入输出函数
	printf("%s %d %.2f\n", tmp.name, tmp.age, tmp.score);
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第26张图片

延申:sprintf 和 scanf

  除了 fprintf / fscanf printf / scanf 这两组格式化输入输出外,还存在另一组格式化输入输出函数:sprintf / sscanf

简单介绍一下,sprintf 是把格式化的数据按照一定的格式转换为字符串,相反的,sscanf 就是从字符串中按照一定格式读取出格式化的数据

C语言进阶——文件操作_第27张图片

C语言进阶——文件操作_第28张图片

sprintf sscanf 可以把结构体中的数据打包成一个字符串,也可以对某个字符串进行拆分。这个东西在我们生活中有应用,比如当我们登录账号时,会把账号、密码这个结构体打包成一串字符串,交给后端处理,当然有个更高级的名词:序列化与反序列化

注意:

  • printf 输出家族返回的是实际写入(输出)的字符总数(包括转义字符),而 scanf 输入家族返回的是实际读取(输入)的元素个数。举个栗子,字符串 abc ,输出返回 3,输入返回 1,因为此时的字符串视为一个元素。
  • 单纯文本数据时,要使用指令 "w" ;单纯数据时,要使用指令 "r" 

fwrite 与 fread

  fwrite 是对文件进行二进制数据的写入fread 是以二进制的形式读取文件中的数据

fwrite

C语言进阶——文件操作_第29张图片

//文件读写之二进制写入
struct S
{
	char name[20];
	int age;
	float score;
}a = { "张三",20,88.8f };

int main()
{
	//把a中的数据写到文件中
	FILE* fp = fopen("test.txt", "wb");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}
	//二进制的写文件
	fwrite(&a, sizeof(a), 1, fp);

	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第30张图片

fread 

C语言进阶——文件操作_第31张图片

//文件读写之二进制读取
struct S
{
	char name[20];
	int age;
	float score;
}tmp;

int main()
{
	//把文件中的数据读取到tmp中
	FILE* fp = fopen("test.txt", "rb");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}
	fread(&tmp, sizeof(tmp), 1, fp);
	printf("%s %d %.2f\n", tmp.name, tmp.age, tmp.score);

	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第32张图片

注意:

  • 当我们使用二进制写入数据到文件时,如果是以文本的方式打开,只能看懂字符串部分,数字部分是看不懂的,我们可以通过VS中的二进制编辑器,来观察其中的数据。
  • 单纯二进制数据时,要使用指令 "wb" ;单纯二进制数据时,要使用指令 "rb" 

随机读写

  随机读写函数,需要配合上面的输入输出函数使用,所谓的随机读写,是指通过改变文件指针的偏移量,来写入或读取数据。介绍三个和随机读取有关的函数:fseek 改变文件指针偏移量ftell 查看当前文件指针的偏移量rewind 使文件指针复原至起始位置

fseek

C语言进阶——文件操作_第33张图片

//fseek,文件指针偏移量
int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}
	char* pc = "abc";
	fseek(fp, 20, SEEK_SET);//从起点往后偏移
	fputs(pc, fp);

	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第34张图片

ftell

C语言进阶——文件操作_第35张图片

//ftell,返回当前文件指针偏移信息
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (NULL == fp)
	{
		perror(fp);
		return 1;
	}
	printf("当前文件指针偏移量为:%d\n", ftell(fp));
	fseek(fp, 20, SEEK_SET);//向后偏移20
	printf("经过fseek设置后的文件指针偏移量为:%d\n", ftell(fp));
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第36张图片

rewind 

C语言进阶——文件操作_第37张图片

//rewind,使文件指针恢复至原位置
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}
	fseek(fp, 20, SEEK_SET);//先让文件指针向后偏移20
	printf("当前文件指针偏移量为:%d\n", ftell(fp));
	rewind(fp);//使文件指针恢复至起始位置
	printf("经过恢复后的文件指针偏移量为:%d\n", ftell(fp));
	return 0;
}

C语言进阶——文件操作_第38张图片

fseek、ftell、rewind 三合一

//fseek、ftell、rewind三合一
//假设文件中存储数据为abcdef
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}
	printf("现在文件中内容为abcdef,我们要依次取出e、b、d\n");
	fseek(fp, -2, SEEK_END);//从后往前偏移
	printf("先取出字符%c\n", fgetc(fp));
	rewind(fp);//还原至起始位置
	fseek(fp, 1, SEEK_SET);//从前往后偏移
	printf("再取出字符%c\n", fgetc(fp));
	fseek(fp, 1, SEEK_CUR);//从当前位置向后偏移
	printf("最后再取出字符%c\n", fgetc(fp));

	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第39张图片

注意:

  • 每进行一次文件输入输出操作,文件指针都会向后移动一位。比如上面的三合一, 当我们读取到字符 'b' 后,文件指针向后移动一位,指向字符 'c' ,此时只需要把文件指针向后偏移一位,就能愉快的读取到字符 'd' 了。

文本文件与二进制文件

文本文件

  文本文件指以ASCII码(文本方式)存储的数据,原始数据机器能直接看懂,将内存中的数据对应ASCII码解码存储后,我们人类也能看懂,举个栗子,在记事本中写的文本,就是文本文件

C语言进阶——文件操作_第40张图片

二进制文件

  二进制文件是将数据编译后转成二进制形式,然后直接存储的文件,这种文件机器能秒懂,读取效率很高(因为不需要转译),但二进制一般人是看不懂的,部分二进制数据也无法通过ASCII码解码为正确的数据,因此强行输出二进制文件,极有可能会得到乱码。比如将上面的那段话通过二进制形式写入文件中,可以看到除字符类型数外,其他类型的数据变成了乱码。

C语言进阶——文件操作_第41张图片

  下图为上面的二进制文件在内存中以二进制形式存储的样子,显示为十六进制(节省空间),实际为二进制

C语言进阶——文件操作_第42张图片

注意 

  • 如果待读取的文件中存储的是二进制数据,就需要使用 二进制读取 "rb" 的形式读取数据;反之如果想写入二进制数据,就需要用 二进制写入 "wb" ,无论是二进制还是普通文本,计算机都能读懂,只是我们看不得罢了。小技巧:可以使用二进制存储重要数据,这样外行人一时半会也理解不了。

文件使用注意事项

被错误使用的feof

  很多人在写C语言课设的时候(学生信息管理系统、通讯录系统等),会通过 feof 来判断文件是否读取结束,这是一种错误的用法,因为 feof 的作用是判断当前文件读取结束原因的,如果是因为读取到了末尾而结束,feof(fp) 就为真;除了这个以外,还有另一个文件读取结算原因判断函数,ferror ferror(fp) 为真时,说明此时发生了读取异常,并非正常结束,我们可以通过这两个报错函数来判断文件读取结束的真正原因。

C语言进阶——文件操作_第43张图片

	char arr[100] = "0";
	fgets(arr, sizeof(arr), fp);
	printf("%s\n", arr);
	int n = 0;
	if ((n = feof(fp)))
		printf("End by EOF\n");
	if (ferror(fp))
		printf("End by IO Error\n");

C语言进阶——文件操作_第44张图片

文件读取结束原因判断

  既然 feof 不是用来判断读取是否结束的,那说明存在其他判断方法,其实答案就是函数设计中,前辈在设计函数时已经考虑好了,比如 fgetc 没有读取到数据会返回EOF,fgets 没有读取到数据会返回NULL,fscanf 可以通过其返回的实际读取元素个数进行判断,fread 可以通过返回值与指定读取的元素数比较。每种读取函数都有属于的自己的判断方法,比如下面这两个例子:

对文本数据进行读取

//读取错误信息判断
//1.文本文件版,假设文件内已有信息,为abcdef
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (NULL == fp)
	{
		perror("fopen::test.txt");
		return 1;
	}

	int ch = 0;//接收读取的数据,要用整型,因为EOF为-1
	while ((ch = fgetc(fp)) != EOF)
	{
		printf("%c", ch);
	}
	//判断是为何结束
	if (feof(fp))
		printf("\nEnd by EOF(因读到文件末尾而结束)\n");
	else if (ferror(fp))
		printf("\nEnd by IO(因中途读取失败而结束)\n");

	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第45张图片

对二进制文件进行读取

//2.二进制文件版
enum { SIZE = 5 };//相当于宏定义
int main()
{
	double a[SIZE] = { 1.1,2.2,3.3,4.4,5.5 };
	//首先把五个浮点数以二进制的形式,写入文件中
	FILE* fp = fopen("testFoBin.txt", "wb");
	if (NULL == fp)
	{
		perror(fp);
		return 1;
	}
	fwrite(&a, sizeof(*a), SIZE, fp);
	fclose(fp);//写入完成,关闭文件

	double b[SIZE] = { 0.0 };
	//现在以二进制的形式读取数据
	fp = fopen("testFoBin.txt", "rb");
	int size = fread(&b, sizeof(*b), SIZE, fp);
	if (size == SIZE)
	{
		printf("No Error!\n");
		int i = 0;
		while (i < size)
			printf("%.2lf ", b[i++]);
	}
	else
	{
		if (feof(fp))
			printf("\nError by EOF\n");
		else if(ferror(fp))
			printf("\nError by IO\n");
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第46张图片

文件缓冲区

  ANSIC 标准定义了“缓冲文件系统”这个概念,所谓缓冲文件系统是指系统自动地在内存中为程序
中每一个正在使用的文件开辟一块文件缓冲区。无论是读取还是写入数据时,都会先将数据送入文件缓冲区,等文件缓冲区装满遇到刷新指令后,数据才会被读取(写入)到目标空间中。文件缓冲区的大小是由编译器决定的。

C语言进阶——文件操作_第47张图片

验证文件缓冲区是否存在

我们可以利用睡眠函数 Sleep 来使程序暂停,此时数据还没有被写入文件中,仍然位于缓冲区;之后再手动刷新缓冲区,数据此时会被推送至文件中

//文件缓冲区
#include
int main()
{
	//打开文件
	FILE* fp = fopen("test.txt", "w");
	if (NULL == fp)
	{
		perror(fp);
		return 1;
	}
	char* ps = "测试文件缓冲区";
	fputs(ps, fp);//先将数据写到缓冲区中
	printf("数据现在已经在缓冲区里面了,但还没有推送到文件中\n");
	printf("程序睡眠10秒,10秒后刷新缓冲区\n");
	Sleep(10000);//睡眠函数,单位是毫秒
	fflush(fp);
	printf("现在缓冲区已经刷新,数据已经写入文件中了\n");
	Sleep(10000);

	//关闭文件,当文件关闭时,缓冲区也会被刷新
	fclose(fp);
	fp = NULL;
	return 0;
}

C语言进阶——文件操作_第48张图片

C语言进阶——文件操作_第49张图片

C语言进阶——文件操作_第50张图片

  可以看到文件缓冲区是真实存在的。

注意:

  • fclose 关闭文件后,会自动刷新缓冲区,数据能够推送至文件
  • 当程序运行结束后,缓冲区也会被自动刷新
  • scanf 遇到 \n 也会触发缓冲区刷新,另外如果其在读取字符型数据时,遇到空白字符(空格TAB键)也会触发缓冲区的刷新

总结

  以上就是C语言文件操作的所有内容了,从文件的打开到文件的关闭,中间可以进行多种操作,构造出巧妙的数据。当然前提是我们得学会文件的相关操作,可以巧记为单字符读写、行读写、格式化读写和二进制读写,无论是那种操作,都需要和对应的文件操作指令匹配上;关于随机读写,记住那三个偏移量函数就行了;最后需要对文件缓冲区有一定的理解,确保数据能成功推送至文件内。总之,文件操作的学习可以宣布毕业了。

  如果你觉得本文写的还不错的话,期待留下一个小小的赞,你的支持是我分享的最大动力!

  如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正

C语言进阶——文件操作_第51张图片

相关文章推荐

C语言进阶——动态内存管理

C语言进阶——自定义类型

C语言进阶——指针进阶试题讲解(万字长文详解)

你可能感兴趣的:(C语言——梦想系列,c语言,c++,学习,文件操作,指针)