【C进阶】文件操作

news2024/11/23 2:59:22

⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C语言进阶
⭐代码仓库:C Advanced
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!

文件操作

  • 前言
  • 一、为什么使用文件
  • 二、什么是文件
    • (一)程序文件
    • (二)数据文件
    • (三)文件名
  • 三、文件的打开和关闭
    • (一)文件指针
    • (二)文件的打开和关闭
      • 1.简单介绍
      • 2.打开关闭代码操作(r只读操作)
      • 3.例子:使用w操作
  • 四、文件的顺序读写
    • (一)用fputc写文件
    • (二)用fgetc读文件
    • (三)用fputs写文件
    • (四)用fgets读文件
    • (五)用fprintf写文件
    • (六)用fscanf读文件
    • (七)写入和读入总结
  • 五、“流”的概念
    • (一)介绍
    • (二)所有输入输出流一部分类型演示
      • 比较
    • (三)总结
  • 六、二进制输入输出
    • (一)用fwrite以二进制形式写文件
    • (二)用fread以二进制形式读文件
  • 七、对比一组有趣的函数
  • 八、文件的随机读写
    • (一)fseek
    • (二)ftell
    • (三)rewind
  • 九、文本文件和二进制文件
  • 十、文件读取结束的判定
    • (一)被错误使用的feof
    • (二)feof与ferror区别
  • 十一、文件缓冲区
  • 总结


前言

文件似乎不是那么很重要,但倘若大家认真学习学习文件,就会发现里面的妙处,我们能通过文件去进行读写等操作然后将我们写的代码进行读和写到我们的磁盘里面,似乎很难的样子,但经过这篇博客的讲解,相信大家会很好的理解文件的操作。
本章重点

  1. 为什么使用文件
  2. 什么是文件
  3. 文件的打开和关闭
  4. 文件的顺序读写
  5. 文件的随机读写
  6. 文本文件和二进制文件
  7. 文件读取结束的判定
  8. 文件缓冲区

这幅图,大家可能看不懂,但当大家学习完下述的知识以后就轻松掌握这张图片,并能够自行画出哦!!!
在这里插入图片描述

PS:我们此篇博客用的都是以下文件:
在这里插入图片描述

一、为什么使用文件

当我们写完程序代码,此时数据是存放在内存中,当程序退出的时候,所写的数据自然就不存在了,等下次运行该程序的时候,数据又得重新录入,如果使用这样的程序就很难受。我们在想既然是程序就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。

二、什么是文件

磁盘里面的东西就是文件:在这里插入图片描述

(一)程序文件

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

(二)数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

在以前我们进行处理文件所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。那我们就开始我们的讲解:

(三)文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如:D:\code\(文件路径)test(文件主干).txt(文件后缀)
为了方便起见,文件标识常被称为文件名。


三、文件的打开和关闭

(一)文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。
每打开一个文件,就会在内存中存放这个文件的相关信息,而这个文件信息区里面的信息是被保存在一个struct FILE;结构体变量中。
在这里插入图片描述
这是FILE的定义:
在这里插入图片描述
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,我们不必关心细节。一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。这是FILE*的指针变量:
在这里插入图片描述
定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。

这个与上述的图相对应,接下来我们看一下文件的打开和关闭:

(二)文件的打开和关闭

1.简单介绍

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件
看看MSDN里面对于fopen和fclose的介绍
fopen就是打开文件:
在这里插入图片描述
在这里插入图片描述

而fclose其实相当于free的操作与做法:
在这里插入图片描述

2.打开关闭代码操作(r只读操作)

大家可能感觉还是不是很了解,那就用代码看一下:

#include<stdio.h>
//D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt - 绝对路径
//如果test.txt和这个工程处在同一个文件夹里,那就是相对路径
int main() {
	FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "r");
	//FILE* pf = fopen("test.txt", "r");
	//打开失败
	if (pf == NULL) {
		perror("fopen->pf");
		return 1;
	}
	//打开成功
	else {
		printf("打开文件成功\n");
	}
	//读文件
**加粗样式**	//……
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

1.D:\GITTE chuantimu\test_1_17(【C进阶】文件操作)\test.txt - 绝对路径
2.如果test.txt和这个工程处在同一个文件夹里,那就是相对路径

如下图:

绝对路径在这里插入图片描述
在这里插入图片描述

相对路径:
打开方式:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.例子:使用w操作

这里必须先介绍一下fputs函数的概念,相信大家肯定一看就懂,无非就相当于一个scanf:
在这里插入图片描述
那我们写代码:

#include <stdio.h>
int main(){
	//打开文件
	FILE* pFile = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "w");
	if (pFile == NULL) {
		perror("fopen->pFile");
		return 1;
	}
	//文件操作
	else {
		fputs("fopen example", pFile);
		printf("文件写入成功\n");
	}
	//关闭文件
	fclose(pFile);
	pFile = NULL;
	return 0;
}

来看效果:
在这里插入图片描述
在这里插入图片描述
ps:这可不是我在记事本里手写的哦!!!


四、文件的顺序读写

先上张图片:在这里插入图片描述
大家可能云里雾里的,那就直接往下看,下面会有详细的介绍和代码:

(一)用fputc写文件

在这里插入图片描述

在这里插入图片描述
大家发现没有这个程序就是上面的程序,我没有在test.txt文件中把原本的内容删除,而直接替换了,这是因为’w’这个读的操作是先把原有数据清空(销毁)再进行写入的,而这个写入是如何写入的呢?是顺序写入。
代码如下:

#include <stdio.h>
int main() {
	//打开文件
	FILE* pFile = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "w");
	if (pFile == NULL) {
		perror("fopen->pFile");
		return 1;
	}
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++) {
		fputc(ch, pFile);
	}
	printf("文件写入成功\n");

	//关闭文件
	fclose(pFile);
	pFile = NULL;
	return 0;
}

(二)用fgetc读文件

在这里插入图片描述

读文件用’r’与fgetc配合,fgetc只能一个字符一个字符读取。
在这里插入图片描述
代码如下:

#include <stdio.h>
int main() {
	//打开文件
	FILE* pFile = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "r");
	if (pFile == NULL) {
		perror("fopen->pFile");
		return 1;
	}
	//读文件
	int i = 0;
	for (i = 'a'; i < 'z'; i++) {
		int ch = fgetc(pFile);
		printf("文件读入成功,结果为>");
		printf("%c\n", ch);
	}
	//关闭文件
	fclose(pFile);
	pFile = NULL;
	return 0;
}

(三)用fputs写文件

在这里插入图片描述
跟fputs差不多,这个是输入字符串的函数,是一整个字符串输进去。
在这里插入图片描述
代码如下:

#include<stdio.h>
int main() {
	FILE* pFile = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "w");
	if (pFile == NULL) {
		perror("fopen->pf");
		return 1;
	}
	//写文件
	//写一行数据
	fputs("hello world!", pFile);
	fputs("hello world", pFile);
	printf("文件写入成功\n");
	//关闭文件
	fclose(pFile);
	pFile = NULL;

	return 0;
}

(四)用fgets读文件

在这里插入图片描述
实际上就是将拿出来的字符存放在新开辟的一个数组中,其中需要注意的是在最后一个字符是\n,所以真正的字符个数为n-1个,所以最多的读出的字符数为n-1个字符。
在这里插入图片描述

#include<stdio.h>
int main() {
	FILE* pFile = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "r");
	if (pFile == NULL) {
		perror("fopen->pf");
		return 1;
	}
	//读文件
	//读一行数据
	char buf[20] = { 0 };
	//读5个是加上\n后5个字符,实际上真正的字符是4个
	fgets(buf, 5, pFile);
	printf("读入成功\n");
	printf("%s\n", buf);
	//关闭文件
	fclose(pFile);
	pFile = NULL;

	return 0;
}

看样子似乎这个函数解决了,可是,我们需要看到源头,当我们写入的时候写入了多行,而读的时候想要把所有的字符都读出来,那我一个字“莽”!直接最大字符数为max,拉满,这下总可以了吧,可是呢,记不记得我们说那个\n,也就是说,当读完第一行的所有字符的时候,读到\n以后就不会继续往下读了,也就是说只读到一行的字符。

(五)用fprintf写文件

看这个fprintf是不是有种printf的既视感,所以,我们与printf进行对比,发现后面都有argument……看似不理解,实际上我们知道,printf是可以有很多个参数的,所以就是可变参数列表,我们直接看图,然后直接书写:
在这里插入图片描述
在这里插入图片描述
代码如下:

#include<stdio.h>
struct S {
	char name[20];
	int age;
	float score;
};
int main() {
	struct S s = { "zhangsan",18,98.5 };
	FILE* pFile = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "w");
	if (pFile == NULL) {
		perror("fopen->pf");
		return 1;
	}
	//写文件
	//写带有格式化地写入文件
	fprintf(pFile, "%s %d %f\n", s.name, s.age, s.score);
	printf("写入文件成功\n");
	//关闭文件
	fclose(pFile);
	pFile = NULL;

	return 0;
}

(六)用fscanf读文件

fscanf与scanf一样,也是有&的,并且也是有可变参数的,所以我们先上张对比图:
在这里插入图片描述
发现无非就多了个pFile指针而已,那就直接上手写:
在这里插入图片描述

#include<stdio.h>
struct S {
	char name[20];
	int age;
	float score;
};
int main() {
	struct S s = { 0 };
	FILE* pFile = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "r");
	if (pFile == NULL) {
		perror("fopen->pf");
		return 1;
	}
	//读文件
	//读取格式化文件
	fscanf(pFile, "%s %d %f", s.name, &(s.age), &(s.score));
	printf("读出文件成功>");
	printf("%s %d %f\n", s.name, s.age, s.score);
	//关闭文件
	fclose(pFile);
	pFile = NULL;

	return 0;
}

(七)写入和读入总结

大家肯定会有个疑惑,我们的scanf是通过键盘去敲的,那个黑框框会出现闪烁的竖线让我们去敲,但是为什么在fscanf里面是"r"读操作!?这似乎有点不符合我们的常理,那我们画一张关系图直接简简单单来理解一下吧!
在这里插入图片描述
我们是程序,在程序的眼中,一个程序员敲键盘是输入到内存当中,用的是读入,所以是scanf,而这个程序员想在屏幕上看看,那就是输出,从内存中输出,那就是写出来;同理,当文件需要进入内存已经,需要输入函数进行读入,那就是fscanf这类了,而从内存输出进入文件里面就需要fprintf这类函数了。
ps:如果大家记不住这些读和写的操作,只需记得fp开头就是写,要用“w”,要是fg和fs开头的就是读,要用"r"操作,是可以打印出来的。


五、“流”的概念

(一)介绍

大家有没有发现一件事情,在第四章节刚开始的那张图片里最右边有个是所有输入流和所有输出流的概念,这个是什么!?那就需要好好介绍了:
在这里插入图片描述

我们知道,计算机内部存储了很多的数据,我们想把它们打印出来,放到文件里面,发到网络里面,放在移动硬盘里面,那我们称文件、网络这些属于输出设备,我们的数据需要放到不同的输出设备中时,放到文件,放到网络里面这些放的方法都是不一样的,那我们作为程序员,写完程序以后,需要把这些东西放到输出设备中,那么多不一样的方法我都要会的话那不是很麻烦吗,那计算机给我们提供了非常好的方法,它给了我们程序员一个很好的封装设备,称为“流”,我们只需要会一种就是把这些程序全部放到流里面去,让流自动进行分流去到不同的输出设备里面。这似乎不是很好理解,那就再举个例子吧!!!我是中国人,说中文,有一天呢,我结婚了,想要发请柬给金钱世界排名前100的富商和各国的世界首富那里去,好家伙,马云、马化腾那边我还能写写中文给他们他们能看懂,但是我给其他国家的很多富豪他们看不懂中文呀!这他们看不懂能来参加婚礼吗!?那肯定不行,此时我们怎么办,是不是需要用一个百度翻译,那我以中文输入给百度翻译,百度翻译自动的翻译成所有国家的语言然后就能发送了,知道了这个概念,大家就能够真正了解流的概念了,其实就是一个承担了翻译的任务,而这个流是FILE结构,是用FILE进行使用的。
在这里插入图片描述

我们说啊,一个C语言程序打开时候,默认是打开了三个‘“流”,分别叫做stdin(标准输入)、stdout(标准输出)、stferr(标准错误)。这三个“流”帮助我们输入输出,学习C语言初期的时候,我们学习的第一个程序就是用scanf,printf来操作的,当时候我不知道这两个玩意是干嘛的,就看,哇好神奇,我输入了3,运行编译出来的还是3,这个真的好神奇,其实,我们进行printf和scanf的时候,计算机已经给我们很有效的工作了,默认的三个“流”帮助我们了已经,这是计算机内部在操作,而我们进行写C语言代码的时候,不需要自行打开流,因为计算机已经默认打开了。
在这里插入图片描述

(二)所有输入输出流一部分类型演示

我们在开头讲啊,fputc,fgetc,fgets,fputs,fscanf,fprintf是可以适用于所有输入输出流,那我们已经在前面演示了用函数输入输出到文件的操作了,那我们接下来试一试用这些函数操作到其他输出设备吧!
(接下来的操作都是在演示我们进行“流”的操作,是看底层的逻辑)

使用stdin和stdout:
在这里插入图片描述

比较

scanf和printf只针对标准输入输出流,而fprintf和fscanf是针对所有输入输出流,也就是即支持标准输入输出流,也支持文件。

(三)总结

1.FILE*指针维护的是程序去找相对应的文件而进行的输入输出流的。也就是可以理解为我们进行读取文件以及写入文件甚至修改文件的时候,我们在这些fputc等的函数中加入此指针即可。
在这里插入图片描述

2.stdin,stdout和stderr这些是程序进行找相对应的屏幕、键盘外设来进行的输入输出流的。也就是我们可以理解为这几个关键字用在和FILE同样的位置就是能够维护的是屏幕和键盘的操作。
在这里插入图片描述


六、二进制输入输出

在这里插入图片描述
如图片所示,只针对文件哦~~

(一)用fwrite以二进制形式写文件

先打开MSDN看一下关于fwrite的介绍:
在这里插入图片描述
这个fwrite怎么参数那么多,我们也在图片里面一一罗列了,大家还记得之前我们提供的一张图片吗在这里插入图片描述
这张图片,所以打开二进制需要根据表格进行查询,那我们直接写代码吧!
在这里插入图片描述
这怎么记事本是这个样子,因为我们存的是二进制,那我们想打印出来二进制转换以后的样子那就需要用下面的fread函数了。
代码:

#include<stdio.h>
struct S {
	char name[20];
	int age;
	float score;
};
int main() {
	struct S s = { "zhangsan",18,98.5 };
	FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "wb");
	if (pf == NULL) {
		perror("fopen->pf");
		return 1;
	}
	//写文件
	fwrite(&s, sizeof(struct S), 1, pf);
	printf("写入二进制文件成功\n");
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

(二)用fread以二进制形式读文件

让我们继续打开MSDN看一看fread的介绍,这个fread与fwrite是一样的参数:
在这里插入图片描述
fread很强大,能够将二进制的形式转码换成我们能够看懂的数,我们看不懂没关系,计算机内部能看懂并能进行转换操作,如下截图:
在这里插入图片描述
那记事本是记录的那么不可思议,那我们就试一试二进制编辑器来看一下都存了些啥吧!
在这里插入图片描述
在这里插入图片描述
哈哈,到这里我们还是看不懂,但对照着表格就能一个字符一个字符分析出来了,这里不过多分析了。

代码:

#include<stdio.h>
struct S {
	char name[20];
	int age;
	float score;
};
int main() {
	struct S s = { 0 };
	FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "rb");
	if (pf == NULL) {
		perror("fopen->pf");
		return 1;
	}
	//写文件
	fread(&s, sizeof(struct S), 1, pf);
	printf("读入二进制文件成功\n");
	printf("%s %d %f", s.name, s.age, s.score);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

七、对比一组有趣的函数

对比对比一下三组函数:
在这里插入图片描述
前面两组我们都能够根据上面的内容能够很简单的理解一下:
在这里插入图片描述

那我们重点要了解的是sscanf和sprintf:
先打开MSDN一看,我们再直接书写代码:
在这里插入图片描述
在这里插入图片描述

代码如下:

#include<stdio.h>
struct S {
	char name[20];
	int age;
	float score;
};
int main() {
	struct S s = { "zhangsan",18,98.5 };
	char buf[100] = { 0 };
	sprintf(buf, "%s %d %f", s.name, s.age, s.score);//将数据放到buf字符数组里面
	printf("%s %d %f\n", s.name, s.age, s.score);//按照字符串打印
	//还原
	struct S tmp = { 0 };
	sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
	printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);//按照结构体打印

	return 0;
}

所以我们可以归纳sscanf和sprintf为:
在这里插入图片描述


八、文件的随机读写

(一)fseek

**作用:**根据文件指针的位置和偏移量来定位文件指针。

我们先来认识一下fseek吧!
在这里插入图片描述
这个fseek是包含了三个参数的,第一个参数是关于与文件进行“流”的操作,第二个参数是偏移量,是关于内存中便宜的地址,第三个参数是分为三个(当前位置,文件末尾和文件起始位置),这看似很难理解,但是我们打开文件会发现文件中有个一直闪烁的光标,这个就是指针,我们可以通过操作偏移量来操作指针指向的位置,大家可能觉得很难理解,那我们直接上图片理解:在这里插入图片描述
代码如下:

#include<stdio.h>
int main() {
	FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "r");
	if (pf == NULL) {
		perror("fopen->pf");
		return 1;
	}
	else {
		int ch = fgetc(pf);//指针指向a
		printf("%c\n", ch);//a
		ch = fgetc(pf);//指针指向b
		printf("%c\n", ch);//b
		ch = fgetc(pf);//指针指向c
		printf("%c\n", ch);//c
		//如果继续往下读,那读的是d,指针指向的是d
		//但是我们调整一下,我们不读d,我们读b
		//fseek(pf, -2, SEEK_CUR);
		fseek(pf, 1, SEEK_SET);
		ch = fgetc(pf);
		printf("%c\n", ch);//b
		fclose(pf);
		pf = NULL;
	}

	return 0;
}

这串代码偏移量很好算,口算即可,但是碰到那种很复杂的怎么算偏移量呢?是个比较棘手的问题,那么计算机又又又懂我们了,给了我们ftell函数,让我们能够进行算偏移量。

(二)ftell

作用:返回文件指针相对于起始位置的偏移量。

在这里插入图片描述
这其实是最简单的了,就直接用文件指针即可:
在这里插入图片描述
这里就给截图即可,代码只需在后面加上打印这个偏移量即可。

(三)rewind

作用:让文件指针的位置回到文件的起始位置。

MSDN看介绍:
在这里插入图片描述

在这里插入图片描述
这里放总代码:

#include<stdio.h>
int main() {
	FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "r");
	if (pf == NULL) {
		perror("fopen->pf");
		return 1;
	}
	else {
		int ch = fgetc(pf);//指针指向a
		printf("%c\n", ch);//a
		ch = fgetc(pf);//指针指向b
		printf("%c\n", ch);//b
		ch = fgetc(pf);//指针指向c
		printf("%c\n", ch);//c
		//如果继续往下读,那读的是d,指针指向的是d
		//但是我们调整一下,我们不读d,我们读b
		fseek(pf, -2, SEEK_CUR);
		//fseek(pf, 1, SEEK_SET);
		ch = fgetc(pf);
		printf("%c\n", ch);//b
		printf("%d\n", ftell(pf));
		rewind(pf);
		ch = fgetc(pf);
		printf("%c\n", ch);
		fclose(pf);
		pf = NULL;
	}

	return 0;
}

九、文本文件和二进制文件

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
一个数据在内存中是怎么存储的呢?
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节

我们假如想存个10000这个数,有两种不同的存储方式,一种是ASCII码值存储,另一种是二进制形式存储,ASCII码值存储是先把10000分隔开5个字符,根据字符在ASCII码值内的转化为10进制数再转化成二进制存储;二进制存储是直接将10000转化成二进制存储起来,似乎很难理解,那就来张图片理解理解:

在这里插入图片描述
字符’1’对应的ASCII码值为49,49的二进制为00110001;字符‘0’也同样。
我们通常看到的内存是十六进制,我们看一下10000在内存中的存储:
在这里插入图片描述
VS是小端存储的,所以拿出来就是0x00 00 27 10 转换成二进制为00000000 00000000 0010011 00010000所以是二进制存储的。
那一个代码测试不过瘾,再来一个:
在这里插入图片描述
在这里插入图片描述


十、文件读取结束的判定

(一)被错误使用的feof

在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

  1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
    例如:
    fgetc 判断是否为 EOF .
    fgets 判断返回值是否为 NULL .
  2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数
    例如:
    fread判断返回值是否小于实际要读的个数。

(二)feof与ferror区别

首先是文件读取结束了
结束后我想知道读取结束的原因
feof - 返回真,就说明是文件正常读取遇到了结束标志而结束的。
ferror - 返回真,这说明是文件在读取过程中出错了而结束。

//测试代码1
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	int c; // 注意:int,非char,要求处理EOF
	FILE* fp = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "r");
	if (!fp) {
		perror("File opening failed");
		return EXIT_FAILURE;
	}
	//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
	while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
	{
		putchar(c);
	}
	//判断是什么原因结束的
	if (ferror(fp))
		puts("I/O error when reading");
	else if (feof(fp))
		puts("::End of file reached successfully");
	fclose(fp);
}

在这里插入图片描述

//测试代码2:
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
	double a[SIZE] = { 1.,2.,3.,4.,5. };
	FILE* fp = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "wb"); // 必须用二进制模式
	fwrite(a, sizeof * (a), SIZE, fp); // 写 double 的数组
	fclose(fp);
	double b[SIZE];
	fp = fopen("test.bin", "rb");
	size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 读 double 的数组
	if (ret_code == SIZE) {
		puts("Array read successfully, contents: ");
		for (int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
		putchar('\n');
	}
	else { // error handling
		if (feof(fp))
			printf("Error reading test.bin: unexpected end of file\n");
		else if (ferror(fp)) {
			perror("Error reading test.bin");
		}
	}
	fclose(fp);
}

十一、文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
如下图演示:
在这里插入图片描述
我们来一个代码体会体会吧!

#include <stdio.h>
#include <windows.h>
//VS2022 WIN11环境测试
int main()
{
	FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_17(【C进阶】文件操作)\\test.txt", "w");
	fputs("abcdef", pf);//先将代码放在输出缓冲区
	printf("睡眠10秒-已经写数据了,请打开test.txt文件,看文件有没有内容\n");//没有内容
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
	printf("再睡眠10秒-此时,再次打开test.txt文件,看文件有没有内容\n");//有内容了
	Sleep(10000);
	fclose(pf);//fclose在关闭文件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}

在这里插入图片描述
所以,因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。


总结

文件操作是十分的有趣的,能够从原本只是闭塞的C语言调试台能够在磁盘中显示出来,是很好用的一种方法,以后就不会担心自己的代码太长无法复制到微信传给朋友了,而现在可以送到文件txt中将这些代码全部C过去,是很好用的!!!


客官,阅读到这儿了,来个三连支持一下吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/170001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序应用生命周期

小程序应用生命周期生命周期介绍应用生命周期钩子函数参数对象页面生命周期页面生命周期-页面参数组件生命周期生命周期介绍 定义 一个组件或者页面生老病死的过程一堆会在特定时期触发的函数 分类 应用生命周期页面生命周期组件生命周期 应用生命周期钩子函数 属性说明onL…

Xpath Helper 在新版Edge中的安装及解决快捷键冲突问题

&#x1f935;‍♂️ 个人主页老虎也淘气 个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f44d;&#x1f3fb; 收藏…

vue2源码分析-keep-alive组件

简介 keep-alive是Vue.js的一个内置组件。它能够将指定的组件实例保存在内存中&#xff0c;而不是直接将其销毁&#xff0c;它是一个抽象组件&#xff0c;不会被渲染到真实DOM中&#xff0c;也不会出现在父组件链中。 具体用法咱们这里就不再细说了&#xff0c;今天主要是探讨…

JavaEE day2 初识web与HTML

初步了解相关知识 关于端口&#xff08;port&#xff09;&#xff1a;一个端口同一时间只能被一个进程监听&#xff0c;但是一个进程可以监听多个端口 URL的标准格式&#xff1a;协议名称&#xff1a;//主机/资源路径&#xff1f;查询字符串#文档片段 一般协议最常见的为htt…

Java基础之《netty(25)—handler链调用机制》

一、netty的handler的调用机制 1、使用自定义的编码器和解码器来说明netty的handler调用机制。 客户端发送long -> 服务器 服务端发送long -> 客户端 2、案例 二、客户端发送给服务端 1、服务端 NettyServer.java package netty.inboundhandlerAndOutboundhandler;i…

【C++】从0到1入门C++编程学习笔记 - 基础入门篇:程序流程结构

文章目录一、选择结构1.1 if 语句1.2 三目运算符1.3 switch语句二、循环结构2.1 while 循环语句2.2 do...while 循环语句2.3 for 循环语句2.4 嵌套循环三、跳转语句3.1 break 语句3.2 continue 语句3.3 goto 语句C/C支持最基本的三种程序运行结构&#xff1a;顺序结构、选择结构…

MySQL进阶——优化

1、选择最合适的字段属性 Mysql是一种关系型数据库&#xff0c;可以很好地支持大数据量的存储&#xff0c;但是一般来说&#xff0c;数据库中的表越小&#xff0c;在它上面执行的查询也就越快。因此&#xff0c;在创建表的时候&#xff0c;为了获得更好的性能&#xff0c;我们…

腾讯云HiFlow场景连接器 联动对象存储企业网盘,打通数据分发“最后一公里”

对云厂商和企业用户来说&#xff0c;随着数据规模的快速增长&#xff0c;企业除了对存储功能和性能的要求不断增加&#xff0c;也越来越注重数据分发的效率。在传统数据分发的过程中&#xff0c;数据管理员往往需要先在存储桶下载对应的客户方案/交付资料&#xff0c;再使用微信…

LINUX软中断-softirq

前言 关于linux的软中断的文章&#xff0c;在网上可以找到很多&#xff0c;但总觉着讲的都不够深入&#xff0c;打算自己写一下 软中断的感性认识 中断一旦被触发&#xff0c;本地cpu正在运行的不管是什么程序都要让路&#xff0c;让中断程序执行并且执行过程中不能被打断。…

分布式事务问题

4.2 分布式事务问题 4.2.1 什么是分布式事务 一次课程发布操作需要向数据库、redis、elasticsearch、MinIO写四份数据&#xff0c;这里存在分布式事务问题。 什么是分布式事务&#xff1f; 首先理解什么是本地事务&#xff1f; 平常我们在程序中通过spring去控制事务是利用…

Linux---进程优先级

目录 基本概念 查看系统进程 PRI and NI 用top命令更改已存在进程的nice&#xff1a; 其他概念 基本概念 cpu资源分配的先后顺序&#xff0c;就是指进程的优先权&#xff08;priority&#xff09;。 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很…

JNPF 3.4.5 快速开发框架源码目录截图 Gitee代码托管和研发协作平台

Gitee Gitee 除了提供最基础的 Git 代码托管之外&#xff0c;还提供代码在线查看、历史版本查看、Fork、Pull Request、打包下载任意版本、Issue、Wiki 、保护分支、代码质量检测、PaaS项目演示等方便管理、开发、协作、共享的功能。 作为一个应用项目&#xff0c;一般会有一…

flink环境参数引起的错误

环境参数&#xff1a;flink使用的版本是1.13.5、CentOS Linux 8一&#xff0c;默认环境引起本地与集群的jar包冲突遇到的情况是在idea执行的时候是没有问题的&#xff0c;然后打成jar包用集群执行的时候就会遇到问题。报错的时候会不太一&#xff0c;总之顺着错误去找的话会找到…

【力学性能预测】基于人工神经网络的钢板力学性能预测(附完整代码和数据集,系列3)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 Hello,大家好,我是augustqi。今天手把手带大家做一个机器学习实战项目:基于人工神经网络的钢板力学性能预测,或者称…

文本生成视频、AI临床知识理解、大模型有趣案例、智源社区《预训练周刊》第70期...

No.70智源社区预训练组预训练研究观点资源活动周刊订阅《预训练周刊》已经开启“订阅功能”&#xff0c;扫描下面二维码&#xff0c;进入《预训练周刊》主页&#xff0c;选择“关注TA”&#xff0c;即可收到推送消息。关于周刊本期周刊&#xff0c;我们选择了12篇来自国内外知名…

《机器人SLAM导航核心技术与实战》第1季:第4章_机器人传感器

视频讲解 【第1季】4.第4章_机器人传感器-视频讲解 【第1季】4.1.第4章_机器人传感器_惯性测量单元-视频讲解 【第1季】4.2.第4章_机器人传感器_激光雷达-视频讲解 【第1季】4.3.第4章_机器人传感器_相机-视频讲解 【第1季】4.4.第4章_机器人传感器_带编码器的减速电机-视频…

Python机器学习:数据探索与可视化(一)

什么是数据探索&#xff1f; 在前面我们说到&#xff0c;所谓机器学习&#xff0c;就是用已知的数据通过算法去预测未来未知的数据。但是这个过程进行的前提就是要保证已知数据的完成性。所以数据探索&#xff0c;就是检查数据是否完整&#xff0c;是否有缺失值。 什么是可视化…

【安全研究】基于OPA和Spring Security的外部访问控制

译者导读 CNCF的毕业项目Open Policy Agent&#xff08;OPA&#xff09;, 为策略决策需求提供了一个统一的框架与服务。它将策略决策从软件业务逻辑中解耦剥离&#xff0c;将策略定义、决策过程抽象为通用模型&#xff0c;实现为一个通用策略引擎&#xff0c;可适用于广泛的业…

阿里云对话 Tapdata:「开发者优先」正在影响商业化软件的开源选择

在刚刚过去的2022年&#xff0c;Tapdata 带着开源项目 PDK&#xff08;Plugin Development Kit&#xff09;及 Tapdata Community 和大家见面&#xff0c;兑现了我们对自己以及开发者们的开源承诺&#xff0c;同时与阿里云等生态伙伴联合&#xff0c;加速构建更加开放的数据生态…

Linux基础 - DNS服务进阶

‍‍&#x1f3e1;博客主页&#xff1a; Passerby_Wang的博客_CSDN博客-系统运维,云计算,Linux基础领域博主&#x1f310;所属专栏&#xff1a;『Linux基础』&#x1f30c;上期文章&#xff1a; Linux基础 - DNS服务基础&#x1f4f0;如觉得博主文章写的不错或对你有所帮助的话…