C语言文件操作函数详解——将你的代码永久化 ( •̀ ω •́ )✧

news2024/11/23 20:03:35

  🎄博客主页:🎐大明超听话

    🎋欢迎关注:👍点赞🙌关注✍评论

    🎍系列专栏:🎑从零开始C语言

                           🎊从0开始数据结构与算法详解

                           🎆计算机考研——从零开始计算机网络

                           🎃C语言游戏制作详解

                           🎉葵花宝典之C语言冷知识

    🎪伙伴们,你们的支持对我真的很重要,欢迎👍点赞✍评论✨关注✨,欢迎各位朋友私信留言随时骚扰,私信必回。感谢你们的支持与转发!

    🎠关注我, 一同分享更优秀的内容吧!ヾ(≧▽≦*)o


🍱🍱本文重点🍱🍱:

     🎢🍛文件操作函数🍛🎢

🦺目录🦺

    🎎预备知识存储

    🎭1.为什么使用文件?🎭

    🎒2.什么是文件?🎒

    👔2.1程序文件👔

    🛵2.2数据文件🛵

    🛹2.3文件名🛹

     🚄2.4文件名表示的两种路径🚄

     🚅2.4.1.绝对路径🚅

    🚈2.4.2相对路径🚈

    🚟3.什么是文件指针🚟

    🛬文件操作函数🛬

    🛫1.文件打开和关闭函数🛫

    🚤文件打开函数:fopen

    🚢文件关闭函数:fclose

    🪂2.文件的顺序读写函数🪂

    🚥2.1 fgetc函数和 fputc函数

     💺2.2 fgets 函数和 fputs 函数💺

    🚁2.3 fscanf 函数和 fprintf 函数🚁

    ⛵3.文件的随机读写⛵

    🚏3.1 fseek函数🚏

    🏡3.2 ftell 函数🏡

    🚂3.3 rewind 函数

    🗼4.文件结束函数的使用🗼

    ⛲4.1 feof 函数的错误使用⛲

      🌌4.2正确判断文件结束🌌

    🚒文本文件和二进制文件🚒 

    🚂1.二进制输出函数fread🚂

    🗻2 二进制输出函数 fwirte🗻 

  🌏文件操作函数思维导图🌏

    🎎预备知识存储

    🎭1.为什么使用文件?🎭

    我们学习结构体时,写了通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。简单的来说文件就是可以将我们的代码永久保存的东西,只要我们学会使用文件就再也不用害怕代码丢失的问题啦!

    🎒2.什么是文件?🎒

    通常情况下我们将存储在磁盘上或者硬盘上保存于同一个文档的数据就叫做文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。

    👔2.1程序文件👔

    包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。简单的来说程序性文件就是我们编写代码的时候需要用到的文件,可以辅助我们代码的编写。

    🛵2.2数据文件🛵

    数据文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。简单的来说就是我们想要读取进入我们的程序的文本文件以及二进制文件都可以叫做我们的数据文件。

    在我们本次博客当中讨论的是数据文件。在以前的C语言中所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。

    🛹2.3文件名🛹

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

     🚄2.4文件名表示的两种路径🚄

     🚅2.4.1.绝对路径🚅

     我们的文件路径主要分为两种一种是绝对路径一种是相对路径。就比如我们从一个根目录(c:\)开始的就叫做我们的绝对路径,因为我们将我们代码的存储路径完全表示出来了。

    🚈2.4.2相对路径🚈

    我们像是只表示 test.txt 的这种形式就叫做相对路径。使用相对路径有一个条件需要我们必须在我们为表示出的路径之下:就像是假如我们已经处于c:\code的文件夹中的时候我们就可以利用相对路径进行标识我们的文件。(这涉及了一部分Linux里面的知识,我们日后会在Linux专题中详细讲述)

    🚟3.什么是文件指针🚟

    缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。所以我们通常所使用的 FILE 类型所创建的变量其实是一个结构体变量,它所指向的内容是我们创建好的文件信息的结构体。

    我们通常可以创建一个FILE*的指针变量:定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。

    🛬文件操作函数🛬

    🛫1.文件打开和关闭函数🛫

    文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。

    🚤文件打开函数:fopen

    函数原型:

    我们可以由上图看到我们的 fopen 函数的第一个参数为我们想要打开的文件的名字,第二个参数是我们想要打开的方式。我们的第二个参数有如下几种不同的选择:

🪂文件打开方式🪂
字符                                        含                义
 “ r ”

   以只读方式,打开本地文件。

   以 “ r ”方式打开的文件,只能读出,而不能向该文件写入数据。该文件必须是已经存在的,若文件不存在,则会出错。

“ w ”

   以只写方式,创建并打开文本文件,已存在的文件将被覆盖。

   以 “ w ”方式打开文件时,无论文件是否存在,都需创建一个新的文本文件,只能写入数据。

 “  a ”    以只写方式,打开文本文件,位置指针移到文件末尾,向文件末尾添加数据,原文件数据保留。若文件不存在,则会创建一个文件。
 “  + ”   与上面的字符结合,表示以读写方式打开文本文件。即可向文件写入数据,也可从文件中读出数据。
“ b ”与上面的字符串结合,表示打开二进制文件。

        最后我们的 fopen 函数会返回一个为 FILE* 的指针(其实就是FILE结构体的指针)我们再创建一个指针变量接收即可。我们已经知道了 fopen 函数的使用细节,之后我们再来通过一组例子来感受我们 fopen 的具体使用是怎么样的吧!

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	else
	{
		printf("文件打开成功");
	}
}

    🚢文件关闭函数:fclose

    上面我们只是简单的打开了一个文件,但是文件在使用完毕的时候就一定得关闭,否则就会造成意想不到的错误。所以我们再来学习一下 fclose 函数。

    函数原型:

    相比于我们上面的 fopen 函数来说我们的 fclose 函数要简单的多了,只需要我们上面打开文件所接受的指针 pf 作为参数传给我们的 fclose 函数即可。我们的 fclose 函数的返回类型是 int ,这是因为我们关闭文件会有失败的情况我们可以通过 fclose 函数的返回值进而判断文件关闭是否成功。

    通过解析我们可以了解到 fclose 函数的返回值在关闭文件成功的时候会返回一个0的值,在关闭文件失败的时候会返回 EOF (也就是 -1)。同样的我们通过代码将感受 fclose 函数的使用方法。

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件

	//文件关闭
	int ret=fclose(pf);
	printf("%d", ret);
	return 0;
}

    🪂2.文件的顺序读写函数🪂

    在上面我们已经学习了文件的打开和关闭的相关的知识,所以我们接下来就来学习要怎样向文件中书写内容。在这一部分我们会学习很多新的函数,会在不同程度上帮助我们文件的书写。

    🚥2.1 fgetc函数和 fputc函数

    通常我们的文件读写函数都是成对存在的,一个函数进行文件内容的读取,一个函数进行文件的写入。我们依旧先通过函数原型学习 fgetc 函数和 fputc 函数。

    函数原型:

    我们的 fgetc 函数的参数是我们之前接收指针的指针变量,之后我们的 fgetc 函数会一个字符一个字符进行文件的读取。之后我们的函数的返回值详解如下:     我们的 fgetc 函数读取文件有如下几种情况:

                 1. 文件未到末尾,成功读取文件将我们的字符的ASCII码值作为返回值。

                 2.文件未到末尾,文件读取失败,fgetc 函数会返回 EOF 即 -1。便于我们检查。

    我们第三行和第四行的内容是我们 feof的使用方面的辨别,稍后我们会详细讲解。既然知道了我们的 fgetc 函数会读取字符串并返回 ASCII 码值,所以我们就可以利用 printf 直接进行内容的打印了。详细操作如下:

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读取文件的内容(事先我们向文件中入abcdef)
	printf("%c\n", fgetc(pf));//a
	printf("%c\n", fgetc(pf));//b
	printf("%c\n", fgetc(pf));//c
	printf("%c\n", fgetc(pf));//d
	//关闭文件
	fclose(pf);
	return 0;
}

     经过上面的验证之后我们就已经学会了 fgetc 函数的使用了,之后我们再来试着向文件中写入内容。

    函数原型:

    向文件中写入内容的操作就需要使用到我们的 fputc 函数。我们可以在函数的原型中看到,我们的 fputc 函数第一个参数为我们想要向文件中写入的内容。(我们的 char 类型数据的实质是一个ASCII 码值,也为 int 类型的)第二个参数是我们的文件指针变量。

    我们如果向文件写入成功就返回我们所写内容的 ASCII 码值。如果失败则会返回 EOF。

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//向文件写入数据
	int i = 0;
	for (i = 'A'; i < 'Z'; i++)
	{
		printf("%c ", fputc(i, pf));
	}
	
	//关闭文件
	fclose(pf);
	return 0;
}

     打开文件进行查看会发现我们的 A~Z 也确实写入了我们的文件当中。

     💺2.2 fgets 函数和 fputs 函数💺

    我们上面使用的 fgetc 函数和 fputc 函数只可以用来依次输入或者是输出一个字符,这时候就会有人会想了,这多麻烦呀。有没有函数可以直接输出一个字符串的呢?这就要说到我们的 fgets 函数和我们的 fputs 函数了。

    函数原型:

    我们这个函数的原型如上图所示,我么可以看到我们这个函数略显复杂。一共有三个参数,第一个参数为一个 char 类型的指针,是我们从文件中读取内容之后将会放到的字符数组的位置。第二个参数使我们的从文件读取的字符的个数,第三个参数是我们的文件指针变量。在这里我们有几个重要的点需要注意:

    1.当我们字符读取的个数大于我们文件中的一行字符的数量的时候,我们最大输出的字符串长度为一行的字符的个数。 

    2.当我们从文件中拿出一个字符串的时候需要将我们的 num 刻意的增大一,因为我们从文件中读取字符串的时候会自动增添一个 ‘ \0 ’ 我们的 ‘ \0 ’ 同样占据我们的 num 的大小。

    3.我们需要创建一个字符数组进行数据的使用,我们可以通过一张图片来增加我们的认识。

     最后我们函数的返回类型是用于判断我们的 fgets 函数是否从文件中成功读取了数据了的。通过资料可以知道:

    我们的 fgets 函数假如成功调用的话就会返回我们字符串的首地址,函数读取失败同样会返回NULL。我们可以将这一点作为函数是否成功调用的关键。

     示例一:

      示例二:

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//从文件中读取字符串(提前像我们的文件中写好内容)
	char ch[20] = "0";
	printf("%s", fgets(ch, 10, pf));
	
	
	//关闭文件
	fclose(pf);
	return 0;
}

     上面就是我们的 fgets 函数的使用方法,之后我们需要学习的是我们的 fputs 函数。

    函数原型:    我们的 fputs 的实际含义和我们的 fgets 函数大致相同,只不过就是方向反了一下。fputs 表示我们需要建立一个字符数组,之后我们需要将字符数组的指针交给我们的函数的第一个参数,第二个参数依旧是我们的文件指针变量。那么我们重新需要认识的只有我们的 fputs 函数的指针了。    对于我们的返回值资料上面是这么描述的:假如我们的 fputs 函数调用成功就会返回一个不是负数的值,假如我们的函数调用失败就会返回一个 EOF 即 -1 。那我们直接通过例子来学习 fputs 函数的使用。

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//向文件中写入信息
	char ch[40] = "hello world\n你好";
	if (fputs(ch, pf) != EOF)
	{
		printf("文件写入成功");
	}
	
	
	//关闭文件
	fclose(pf);
	return 0;
}

    那么我们的 fgets 函数和我们的 fputs 函数也就介绍完毕了,让我们抓紧时间进入我们下一部分知识的讲解! 

    🚁2.3 fscanf 函数和 fprintf 函数🚁

    在学习完我们的字符串与文件的转化函数之后肯定会有人产生好奇:难道只能是字符串才能写入文件当中吗?要是不是字符串的话应该怎么与文件进行转化呢?别着急,我们接下来就来学习我们的按格式输入和输出文件函数。

    函数原型:

    看过函数原型之后我们可能会对这个函数感到很迷茫,为什么参数中会省略号了?我们可以通过对比我们比较熟悉的 scanf 进行理解:     我们会发现我们 fscanf 函数和我们的 scanf 的函数原型很像,只不过是前面多了一个文件指针,这时候我们就会像会不会和我们的 scanf 函数的用法其实是一样的呢?事实也确实是这样。我们在这里再向大家介绍一个概念:

      可变参数: 可变参数指的是我们函数的参数可以通过我们的输入发生改变,大小并不确定,就像是我们的 scanf("%d %s %d %d",ret1,ch,ret2,ret3); 我们可以根据自己想要输入的需求进行参数长度的任意变化,这也就是我们函数原型中的参数带有省略号的原因。

    这么一来我们的 fscanf 函数的参数也全部介绍完毕了。那我们在来认识一下这个函数的返回类型。

    当我们按一定的格式进行读取文件中的内容的时候,假如读取成功我们的 fscanf 就会返回我们读取数据的项数,(可以忽略不计)如果读取失败就会返回一个 NULL。所以通常情况下我们可以通过判断 fscanf 函数的返回值是否为 EOF 进而判断我们的函数是否使用成功。 

    需要着重强调的是我们 fscanf 函数在使用之前需要提前创建好一个与我们想要读取的文件信息格式相同的结构体,便于我们的信息在我们的屏幕上的输出和打印。

#include<stdio.h>

struct test
{
	char name[20];
	int age;
	float num;
};
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//从文件中读取字符串(提前在我们的文件中写好内容)
	struct test s1;
	fscanf(pf, "%s %d %f", s1.name, &(s1.age), &(s1.num));
	printf("%s %d %f", s1.name, s1.age, s1.num);
	
	//关闭文件
	fclose(pf);
	return 0;
}

    和我们 fscanf 函数相对应的就是我们的 sprintf 函数了,使用 fprintf 函数可以按照指定格式向我们的文件中写入数据。

    函数原型: 

    由于我们上面我们已经认识了 fscanf 函数,已经有了可变参数的概念之后我们的 fprintf 函数的理解起来就简单多了。我们的使用的方法和我们的 printf 函数依旧相同,同样需要增加一个文件指针作为参数。那我们只剩下返回值需要重新认识了。

    通过资料我们可以了解得出:我们的 fprintf 函数在使用的时候如果向文件中写入数据就会返回写入数据的元素数据的个数,如果失败就会返回一个负数,我们同样可以抓住这一点进行判断函数是否调用成功。 

#include<stdio.h>

struct test
{
	char name[20];
	int age;
	float num;
};
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//向文件中写如一定格式的数据
	struct test s1 = { "李四",26,88.88 };
	if (fprintf(pf, "%s %d %f", s1.name, s1.age, s1.num) > 0)
	{
		printf("文件写入成功");
	}
	//关闭文件
	fclose(pf);
	return 0;
}

     虽然我们浮点数的表示并不精确(浮点数在内存中存储就是不精确的)但是我们可以发现我们的数据也按照我们的要求成功写入了我们的文件当中。

    ⛵3.文件的随机读写⛵

    到上面为止我们文件的顺序读写的相关内容就已经全部讲完了。我们会发现我们上面的函数的使用大都是在我们文件的末尾进行文件内容的添加以及删除,但是针对于我们比较长的文件这样书写就会显示出一系列的弊端,我们不可能只从末尾读取或写入文件。所以为了解决这种问题我们的文件操作函数不仅包括了文件的顺序读写函数还包括了文件的随机都写的函数。

    🚏3.1 fseek函数🚏

     我们第一个要认识的随机读写函数就是 fseek 函数,同样的道理我们通过函数的原型来初步认识我们的 fseek 函数。

    函数原型:

    通过我们的函数原型中的参数可以看到,我们的 fseek 函数第一个参数和我们的文件顺序读写所用到的函数一样也为一个文件指针, 第二个函数是一个长整型类型的数据,叫做我们文件的偏移量,表示我们所要查找的内容在文件中的位置,第三个参数 int origin 我们可以将其理解为是一个常量,因为这个参数经常取三种情况:

    就像是上图表示的那样,我们的 origin 通常向函数中传参为 SEEK_SET,(从文件的开始位置开始查找字符)SEEK_CUR,(从文件的当前位置开始查找)和我们的SEEK_END。(从文件的末尾位置开始查找)。就比如说 fseek( pf ,7,SEEK_SET); 就表示我们需要从文件的开始向后跳过七个比特位进行对文件进行更改或者读取操作。 我们这个函数的返回值如下:

    当我们函数成功指向的时候就会返回0,如果执行失败的话就会返回一个非0的值。我们同样可以从我们函数的返回值中判断函数是否成功调用。 函数的使用如下:

#include<stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//更改文件的读取位置,进行文件的读取
	fseek(pf, 7, SEEK_SET);//从文件的开头进行读取数据
	char ch[20] = { 0 };
	fgets(ch, 4, pf);
	printf("%s", ch);
	//关闭文件
	fclose(pf);
	return 0;
}

    🏡3.2 ftell 函数🏡

    在了解过我们的文件偏移函数的使用之后我们肯定会想的是那我们应该怎样的到我们每一个信息的偏移的位置呢?难道要我们一个一个数吗?NONONO,接下来我们就来认识一下我们的 ftell 函数,这个函数可以告诉我们在文件中具体信息的偏移量是多少我们就可以结合我们的 fseek 函数,一起判断使用了。

    函数原型:

    通过上面的函数原型我们可以知道我们的 ftell 的函数参数仅仅只有一个文件指针,那我们究竟应该怎么使用这个函数呢?ftell 函数可以将我们当前文件的偏移作为我们的返回值返回给我们,我们可以通过变量进行接收,进而使用。 我们通过一个例子来理解一下 ftell 函数的具体的使用的方法:

#include<stdio.h>
int main()
{
	//打开一个文件
	FILE* pf1 = fopen("test.txt", "w");
	if (pf1 == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	//向文件中写入一部分数据
	char ch1[20] = "abcdef";
	fputs(ch1, pf1);

	//判断当前位置的偏移量
	int ret=ftell(pf1);

	//重新输入一组数据
	char ch2[20] = "12345";
	fputs(ch2, pf1);

	//关闭文件
	fclose(pf1);
	pf1 = NULL;

	//打开文件
	FILE* pf2 = fopen("test.txt", "r");
	if (pf2 == NULL)
	{
		perror("fopen");
		return 1;
	}

	//调整偏移量
	fseek(pf2, ret, SEEK_SET);

	//读取一组数据
	char ch3[20] = { 0 };
	fgets(ch3, 6, pf2);
	printf("%s", ch3);

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

     我们先通过进行第一次写入之后利用 ftell 函数获得当前位置的偏移量,之后再进行第二次文件写入,之后关闭文件。(注意正打开文件的方式要保持一致,“ r ” 就只能读文件,“ w ”就只能写文件,否则会发生错误!)重新以只读方式打开文件即可进行文件读取。

    我们的ftell函数不仅可以用来打印固定位置的文件数据还可以用来判断一个文件所占的字节的个数,就像是我们下面例子中所展示的一样:

#include<stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");

	//调整光标到文件的末尾
	fseek(pf, 0, SEEK_END);

	//计算当前文件的偏移量
	int ret=ftell(pf);
	printf("%d", ret);

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

    🚂3.3 rewind 函数

    接下来我们要介绍的就是我们的 rewind 函数,其实这个函数在单独使用的时候作用并不大,只是将我们文件读取的位置更改到从我们的文件的开头开始,其作用也可以被我们的 fseek 函数所替代。所以我们在这里只是简单的认识一下这个函数就可以了。

    函数原型: 

    我们只需要向函数的参数中传入我们之前的文件指针即可。这个函数没有返回值。使用方法和上面的 fseek 函数的 SEEK_SET 参数的效果等价。可以作为我们文件内部读取位置的调整方法之一。 

    🗼4.文件结束函数的使用🗼

    ⛲4.1 feof 函数的错误使用⛲

    其实这个函数在大多数情况都是被用来判断文件是否读取到末尾,但是实质上我们的 feof 函数的主要功能并不是这样。还记得我们上面在认识函数的时候向大家介绍的函数的返回值吗?

     我们在上面说到了有一部分的返回值细节我们并没有多说,也就是我们这一部分要向大家解释的内容。我们的 feof 其实并不是用来检测是否到达文件的末尾的函数而是用来检测文件读取是否正常结束的。假如我们读取一个文件,但是我们在文件的中读取发生了错误。所以我们的系统就会自动将检测其置为 ferror 。

     我们的 feof 函数就会不为0的数字,如果没到文件没有读取到末尾或者没有被系统置为 feof 函数项,就会使用我们的 ferror 函数进行进一步的判断。

     我们的 ferror 函数也是一样的,假如文件读取失败的话就会返回一个非零的值(真),如果为0的话就会返回 0(假)。

    总结一下:

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

        文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
                 例如:
                           fgetc 判断是否为 EOF .
                          fgets 判断返回值是否为 NULL

      🌌4.2正确判断文件结束🌌

    有了上面的铺垫之后我们就可以知道我们要想判断文件是否结束,那么就需要使用文件读取函数的返回值进行判断,而不是利用函数 feof 进行判断。那么我们接下来旧居一个例子帮助理解:

#include<stdio.h>
int main()
{
	//写的形式打开文件
	FILE*pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//向文件中写入数据
	int i = 0;
	for (i = 'A'; i <= 'Z'; i++)
	{
		fputc(i, pf);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;

	//以读的形式打开文件
	FILE* pa = fopen("test.txt", "r");
	if (pa == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读取文件
	char ch[50] = "0";
	fgets(ch, 10, pa);

	//判断是否文件读取正常读到末尾
	if (feof(pa)) //正常结束为非0
	{
		printf("文件正常到末尾");
	}
	else if (ferror(pa)) //异常为非0
	{
		printf("文件读取异常");
	}
	else
	{
		printf("文件读取未结束");
	}

	//关闭文件
	fclose(pa);
	pa = NULL;

	return 0;
}

     我们向文件中放入 A~Z 如上图所示,之后我们读取30个字符(多出部分函数自动结束)运行结果是文件读取正常结束并到达末尾,我们读取10个字符,运行结果是文件还未读取完毕。上面就是我们 feof 函数正确的使用的方法,我们可以利用上面所示的方法判断文件是否正常结束。

    🚒文本文件和二进制文件🚒 

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

    通常情况下我们的文本文件在外部可以看看懂文件中所写的内容,但是我们的二进制文件就很难看懂文件所存入的内容了。具有一定的外部保密性。但是保密性又不是那么的强,因为对于高技术的程序员来说将我们的二进制文件转换成为文本文件简直是小菜一碟。由于我们的文本文件和二进制文件的读取和写入的函数不同。我们再来学习一下二进制文件读取写入文件时所需要的函数。

    🚂1.二进制输出函数fread🚂

     同样的道理学习一个新的函数需要从我们的函数原型开始。

     函数原型:

    通过函数原型的显示我们可以发现我们函数的参数一共有四个。第一个参数和我们的 fgets 函数的功能很相似,都是一个用于保存我们数据的指针。只不过返回类型是 void * 类型的, 也就是说我们可以像函数中传入任意的数据类型,在函数的内部我们的数据会自动转化(就像是我们的qsort函数的实现一样)第二个参数是我们想要拿出的元素的单个大小,第三个参数是我们拿出元素的个数,第四个参数是一个文件指针。最后是我们函数的返回类型

    我们的 fread 函数的返回值为我们所读取到的元素的个数,假如我们文件中的元素数量不够的话就会返回读到的元素的个数,我们也可以根据这个特点进行判断二进制文件是否读取完毕。由于我们二进制文件的读取的最好是二进制文件,所以我们再次先了解完 fwrite 函数之后再来给大家展现代码效果。

    🗻2 二进制输出函数 fwirte🗻 

    我们的二进制输出函数是一个向文件中写入二进制数据的函数。我们利用这个函数可以将我们程序中的 int 类型,char 类型等等的数据以二进制的形式写入文件当中。文件中存储二进制的时候我们是不一定能够从文件中读懂的。这是因为假如我们存储的原本就是 char 类型的数据的话,我们的 ASCII 码原本就是二进制,就不需要转换,如果是其它类型的数据的话就需要转换,就读不懂。

    函数原型:

    我们可以发现的是我们 fwrite 函数的原型的参数和我们的 fread 函数的参数一样。两者之间的含义其实也是一样的,只不过 fwrite 函数的效果是将我们的 fread 函数反过来。

    两者之间的关系 就像是我们上图中所表示的一样。那么对于 fwrite 函数的参数我们就不做过多的介绍了。直接让我们来看看 fwrite 函数的返回值。     我们可以通过资料读出,我们的函数假如成功写入的话就会返回成功写入元素的个数,也就是我们和我们的 count 参数大小相同,假如我们的函数写入失败的话就会返回一个于我们的 count 参数大小不同的值,并将我们的 ferror 的值置为1,表示我们文件写入异常。那么接下来让我们结合一下 fread 函数和我们的 fwrite 函数举一个例子:

#include<stdio.h>

struct test
{
	char ch[10];
	int age;
	float num;
};

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//向文件中写入数据
	struct test s1 = { "张三",17,16.6 };
	fwrite(&s1, sizeof(s1), 1, pf);

	//关闭文件
	fclose(pf);
	pf = NULL;

	//打开文件
	FILE* pa = fopen("test.txt", "rb");
	if (NULL==pa)
	{
		perror("fopen");
		return 1;
	}
	//从二进制文件中读取数据
	struct test s2={0};
	//fread(&s2, sizeof(struct test), 1, pa);
	fread(&s2, sizeof(struct test), 1, pa);
	printf("%s %d %f",s2.ch,s2.age,s2.num);

	//关闭文件
	fclose(pa);
	pa = NULL;


	return 0;
}

   🎇 结尾:🎇

      到此为止我们本次博客也就正式结束了,感谢您的观看。创作不易,关注博主为你带来更加优质的作品。那么祝您天天开心,下次再见啦!( •̀ ω •́ )✧

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

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

相关文章

JavaScript中的原型链

本文作者为奇舞团前端开发工程师概述JavaScript 是 Web 的编程语言&#xff0c;简单易学&#xff0c;功能强大&#xff0c;但由于过于灵活设计理念&#xff0c;导致初学者经常一脸懵&#xff0c;本文要谈的是JavaScript中难点之一原型链。原型链的前世JavaScript的诞生要理解Ja…

Nessus介绍与安装

Nessus介绍与安装 1.Nessus简介 Nessus号称是世界上最流行的漏洞扫描程序&#xff0c;全世界有超过75000个组织在使用它。该工具提供完整的电脑漏洞扫描服务&#xff0c;并随时更新其漏洞数据库。Nessus不同于传统的漏洞扫描软件&#xff0c;Nessus可同时在本机或远端上遥控&…

测试开发 | Dubbo 接口测试原理及多种方法实践总结

image1080478 86.9 KB 1、什么是 Dubbo&#xff1f; Dubbo 最开始是应用于淘宝网&#xff0c;由阿里巴巴开源的一款优秀的高性能服务框架&#xff0c;由 Java 开发&#xff0c;后来贡献给了 Apache 开源基金会组织。 下面以官网的一个说明来了解一下架构的演变过程&#xff0…

初学Java中的方法,看这篇就够了

本篇介绍了Java中方法的概念以及方法的使用(方法的定义和调用,实参和形参的关系).方法重载的介绍和使用,编译器如何实现方法重载- -方法签名,介绍和使用方法调用自身解决问题的技巧–递归 对比递归和循环的优缺点 掌握Java中的方法一.方法的概念及使用1.什么是方法2.方法的使用…

【C++】AVL树

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;AVL树&am…

【Linux】CentOS、CentOS Stream、RedHat 和Fedora 之间的关系

目录 简单说明 详细说明 红帽&#xff08;Red Hat&#xff09;系和德班&#xff08;Debian&#xff09;系 简单说明 在centos8之前&#xff1a; Fedora 》RedHat 》CentOS Fedora 是RedHat的“试验场”&#xff0c;很多新功能和特性先加入Fedora 稳定后再加入RedHat&…

YOLOv5 引入 最新 BiFusion Neck | 附详细结构图

YOLO 社区自前两次发布以来一直情绪高涨&#xff01;随着中国农历新年2023兔年的到来&#xff0c;美团对YOLOv6进行了许多新的网络架构和训练方案改进。此版本标识为 YOLOv6 v3.0。对于性能&#xff0c;YOLOv6-N在COCO数据集上的AP为37.5%&#xff0c;通过NVIDIA Tesla T4 GPU测…

99.恢复二叉搜索树

99.恢复二叉搜索树 1、题目描述 题目的额外要求是: 使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用 O(1) 空间的解决方案吗&#xff1f; 2、解题思路 二叉搜索树中某两个节点被交换了数值&#xff0c;那么对中序序列的影响如下&#xff1a; 假设没有交换之前二叉…

活动星投票千人共读一本书网络评选微信的投票方式线上免费投票

“千人共读一本书”网络评选投票_视频投票评选_投票统计小程序_微信不记名投票用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务&am…

正则表达式和re模块

目录 一.基础匹配 1.什么是正则表达式 re模块三个基础方法 re.match(匹配规则&#xff0c;被匹配字符串) search(匹配规则&#xff0c;被匹配字符串) findall(匹配规则&#xff0c;被匹配字符串) 小结 二.元字符匹配 单字符匹配: 示例: 数量匹配 边界匹配 分组匹配…

【Java】【系列篇】【Spring源码解析】【三】【体系】【BeanFactory体系】

BeanFactory体系BeanFactory整体结构体系图顶层接口-BeanFactory1.1、描述1.2、方法解析&#xff08;15个&#xff09;1.2.1、属性1.2.2、获取bean实例1.2.3、获取bean的提供者&#xff08;对象工厂&#xff09;1.2.4、判断是否包含bean1.2.5、单例,原型,bean类型的判断1.2.6、…

SAP ABAP——SAP包(二)【CTS | 传输请求】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计专业大二本科在读&#xff0c;阿里云社区专家博主&#xff0c;华为云社区云享专家&#xff0c;CSDN SAP应用技术领域新兴创作者。   在学习工…

CentOS7.9配置Nginx反向代理+NodeJS部署上线

Nginx配置 Nginx是一个高性能的HTTP和反向代理服务&#xff0c;许多的大型网站都会采用Nginx来进行HTTP服务器托管 安装编译环境gcc g 进入到root目录&#xff1a; yum -y install make zlib zlib-devel gcc-c libtool openssl openssl-devel 安装PCRE PCRE功能时让Ngi…

ue4c++日记3(碰撞报告碰撞位置|力和扭矩)

目录 代码速查 根据世界方向/局部方向移动 根据世界方向/局部方向旋转 加力加扭矩 1碰撞并报告碰撞位置 两个设为阻挡才会阻挡&#xff0c;其中一个是重叠就会重叠 2例子&#xff1a;旋转前进&#xff01;/原地踏步&#xff1f; 3增加力和扭矩 1.头文件 2.cpp文件 …

(14)go-micro微服务服务层Handle开发

文章目录一 Handle层开发功能说明需要完成的服务开发功能&#xff1a;从哪找需要开发的功能二 代码编写三 最后一 Handle层开发功能说明 需要完成的服务开发功能&#xff1a; 登录注册查询用户信息修改信息发送注册邮件发送重置密码邮件重置密码获取权限修改权限退出账号删除…

【计算机视觉】梯度消失和爆炸以及解决方法

问题 梯度消失无论是笔试还是面试都是常客了,其实对应于梯度消失,还有一个梯度爆炸的概念,这又是什么导致的呢?下面我们将根据公式推导来解释何为梯度消失与梯度爆炸。 梯度消失和梯度爆炸的表现 网络层数越多,模型训练的时候便越容易出现 梯度消失(gradient vanish) 和…

史上最全| 14种自动化分拣系统合集

导语大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;你的老朋友&#xff0c;老K。新书上市《智能物流系统构成与技术实践》2023年度-厂商宣传合作位--->点击详情本文为研习社原创&#xff0c;违规转载必究01移栽式02偏转轮式03扫臂式04滑靴式05侧向翻转06推…

C++设计模式(3)——抽象工厂模式

抽象工厂模式 亦称&#xff1a; Abstract Factory 意图 抽象工厂模式是一种创建型设计模式&#xff0c; 它能创建一系列相关的对象&#xff0c; 而无需指定其具体类。 问题 假设你正在开发一款家具商店模拟器。 你的代码中包括一些类&#xff0c; 用于表示&#xff1a; …

Vue3系列二:如何实现对响应式数据的代理

上一篇文章中&#xff0c;我们讲解了 Vue3 中对响应式系统的实现&#xff0c;本章节会更进一步的从数据层面分享 Vue3 中对响应式数据是如何进行代理的&#xff0c;本文主要从引用类型数据和基本类型数据两个方面进行讲解。 实现数据代理的基础 理解 Proxy 和 Reflect 首先&…

26.Isaac教程--导航算法

导航算法 本节详细介绍导航算法。 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录导航算法全局路径规划器规划器模型可见性图算法优化器轨迹规划器全局路径规划器 Isaac 框架中的全局规划器问题被分解为三类&#xff1a;规划器模型、…