【C语言】看了这篇文章,如果你还不会文件操作的话,我把这篇文章给吃了(doge)

news2024/11/15 23:03:16

🚩write in front🚩   

🔎大家好,我是謓泽,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
🏅2021年度博客之星物联网与嵌入式开发TOP5~2021博客之星Top100~2021博客之星Top63~作者周榜84﹣作者总榜704~阿里云专家博主 & 阿里云星级博主~掘金优秀创作者⇿InfoQ创作者⇿51CTO红人⇿全网访问量50w+🏅
🆔本文由 謓泽 原创 CSDN首发🙉 如需转载还请通知⚠
📝个人主页-謓泽的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​
📣系列专栏-
【C】系列_謓泽的博客-CSDN博客🎓
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本📩

文件操作⇢目录

🚩write in front🚩   

🍑🍑🍑为什么使用文件🍑🍑🍑

🍓🍓什么是文件🍓🍓

🍉程序文件🍉  

🍉数据文件🍉

🍉文件名🍉

🍓🍓文件指针🍓🍓

🍑🍑🍑文件的打开和关闭🍑🍑🍑 

🍑🍑🍑fopen() 打开文件🍑🍑🍑

🍓🍓参数的介绍🍓🍓

🍉返回值🍉

🍓🍓fclose() 关闭文件🍓🍓

🍑🍑🍑fopen()代码示例🍑🍑🍑

🍑🍑🍑文件的顺序读写🍑🍑🍑

🍉fgetc() → 字符输入函数🍉 

🍉fputc() → 字符输出函数🍉 

🍓🍓 fputc() 代码示例🍓🍓

🍉"三个流"🍉 

🍑🍑​​​​​​​🍑文件操作函数代码示例🍑🍑​​​​​​​🍑

🍓🍓 fgetc() 代码示例​​​​​​​🍓🍓

🍓🍓fputs() 写入"字符串"🍓🍓

🍓🍓fgets()从流中获读取"字符串" 🍓🍓

🍓🍓fprintf()格式化输出函数🍓🍓

🍓🍓fscanf()格式化输入函数🍓🍓

🍑🍑​​​​​​​🍑fread()🍚fwrite()二进制读/写函数🍑🍑​​​​​​​🍑

🍑🍑​​​​​​​🍑fwrite()🍚fread()代码示例🍑🍑​​​​​​​🍑

🍑🍑​​​​​​​🍑文件的随机读写🍑🍑​​​​​​​🍑 

🍓🍓fseek() - 重新定位流位置指示器🍓🍓

🍓🍓ftell() - 获取流中的当前位置🍓🍓

🍑🍑​​​​​​​🍑文本文件和二进制文件🍑🍑​​​​​​​🍑 

🍉文本文件🍉→

🍉二进制文件🍉→

🍓🍓讲解desu🍓🍓

🍑🍑​​​​​​​🍑文件读取结束的判定🍑🍑​​​​​​​🍑

🍉错误使用 feof()🍉 

🍓🍓文件缓冲区🍓🍓

🔥最后🔥 


🍑🍑🍑为什么使用文件🍑🍑🍑

首先来说下为什么使用文件操作吧,在前面的内容写过一篇通讯录的文章,实际上那个通讯录哪怕我们能够使用动态内存分配给它完成了。但是,依旧存在很大的问题。

实际上在通讯录那个程序当中,我们给 cmd 当中输入指令的时候。此时的数据是会被存放在🍅内存🍅当中的。当我们程序要退出的时候,我们在通讯录当中所输入的数据就自然而然的不存在了。当我们需要下次运行的时候,数据又要重新输入。那么此时这就非常的难受。因此,我们输入的数据当中必须是要放在🍅硬盘🍅当中才能够保存数据。

📢我再来打个比方吧📢:我们玩游戏的时候,有些游戏是不是需要保存当前数据。此时,当我们玩了半个小时的游戏。我们需要进行保存数据是吧,不然你这半小时所玩的东西就会丢失,也就是游戏当中不会替你去保存当前的数据。就会回到你上一次所保存的数据,张三同学不知道我这样说你是否明白了(doge)(☆-v-)

于是,我们想要实现数据不丢失的话,就必须要了解这个文件操作的使用,明白了mie张三同学。 


🍓🍓什么是文件🍓🍓

文件实际上就是我们在硬盘上,所看到的这些东西都是文件,包括[C]盘当中也都是文件。

但是在我们的程序设计当中一般都是会有两种文件的↓

  • 🍉程序文件🍉  

包括源文件的后缀名.c

目标文件(Windows环境后缀为.obj)

可以执行程序(Windows环境后为.exe)

那么张三你现在应该知道编译运行过程是什么了吧,张三:不知道,我💥💥💥

那么这里说下实际上也就是上面按照顺序的编译过程:

.c源程序[编译到],obj目标文件再[链接],exe执行文件。

所以,计算机是不可以直接执行由任意高级余元编写的程序.  


  • 🍉数据文件🍉

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


🍉文件名🍉

文件名是文件存在的标识,操作系统根据文件名来对其进行控制和管理。不同的操作系统对文件命名的规则略有不同,即文件名的格式和长度因系统而异。为了方便人们区分计算机中的不同文件,而给每个文件设定一个指定的名称。由文件主名和扩展名组成。

文件名包含③部分:🍅文件路径🍅+🍅文件名主干🍅+🍅文件后缀🍅

例如:c:\code\test.txt ,为了方便起见,文件名表示通常被称之为文件名。


🍓🍓文件指针🍓🍓

在缓冲文件系统当中,关键的概念实际上是"文件类型指针",简称"文件指针"

每个被使用的文件都在内存中开辟了一个相对应的文件的信息区,用来存放文件的相关信息。这些信息都是保存在一个结构体变量当中的,该结构体是由系统进行声明的,取名为:🍅FILE🍅

下面就是 vs 2013 当中编译环境所提供的 stdio.h 头文件中有以下文件类型声明↓

#ifndef _FILE_DEFINED
struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;//重命名产生

当然,这个 FILE 在不同编译器上的设计可能是不同的,不是绝对的。 

每当我们打开一个文件的时候,系统会根据文件的情况自动会创建一个FILE结构的变量,并进行填充其中的信息。当然我们在使用的时候无需关心这些。

一般都是通过一个FILE的指针来进行维护这个结构体变量,使用起来更加方便。

此时,我们就可以创建一个FILE*的指针变量↓

FILE* pf;

定义 pf 是一个指向FILE类型数据的指针变量。可以使 pf 指向某一个文件的文件信息区也就是指向 FILE 当中结构体的信息。那么通过该文件的信息区就可以访问该文件。也就是说,通过文件指针变量能够找到与它所相互关联的文件。 


🍑🍑🍑文件的打开和关闭🍑🍑🍑 

文件在读写的时候就应该先🍅打开文件🍅

文件在使用结束之后就应该🍅关闭文件🍅

在编写程序的时候,在打开文件的同时,都会返回一个🍅FILE🍅的指针变量指向的文件,也就相当于建立了指针和文件的关系。 

ANSIC 规定了使用 fopen() 函数来打开文件,同时 fclose() 函数是用来关闭文件的。

那么接下来介绍下 🍅fopen()🍅


🍑🍑🍑fopen() 打开文件🍑🍑🍑

📢fopen() 函数声明方式如下↓

FILE * fopen ( const char * filename, const char * mode );

📢打开文件的方式↓

打开文件名在参数🍅filename🍅中指定的文件,并将其与一个关联起来,该流可以在将来的操作中由返回的🍅FILE🍅指针识别。

  • 这个时候张三同学说"流"是什么啊。
  • 张三同学不要着急,刚想说👻那这里说说什么是"流"。

"流":是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为"流"。

流上允许的操作以及如何执行这些操作由🍅mode🍅参数定义。

如果已知返回的流不引用交互式设备(参考🍅setbuf → 设置流缓冲区🍅),则默认情况下它是完全缓冲的。

返回的指针可以通过调用🍅fclose🍅文件解除关联。所有打开的文件在正常程序终止时自动关闭。

🍓🍓参数的介绍🍓🍓

filename → 包含要打开的文件名称的C语言字符串。它的值应该遵循运行环境的文件名规范,并且可以包含一个路径(如果系统支持的话)。

mode → 包含文件访问模式的C语言字符串。它可以是如下所示↓

  1. "r" read(只读):打开文件进行输入操作。该文件必须存在,文件不在error。
  2. "w" write(只写):为输出数据,打开一个文本文件。如果指定的文件不存在的话,则会建立一个新的文件。
  3. "a"追加:打开文件,在文件的末尾输出。输出操作总是在文件的末尾写入数据,并展开它。重新定位操作(fseek, fsetpos,倒带)被忽略。如果文件不存在,则创建该文件。
  4. "r+"读取/更新:打开一个文件进行更新(包括输入和输出)。该文件必须存在。
  5. "w+"写入/更新:创建一个空文件并打开以进行更新(包括输入和输出)。如果一个同名的文件已经存在,它的内容将被丢弃,并且该文件将被视为一个新的空文件。
  6. "a+" append/update(读写):打开一个文件进行更新(包括输入和输出),所有输出操作都在文件的末尾写入数据。重新定位操作(fseek, fsetpos, rewind)影响下一个输入操作,但输出操作将位置移回文件末尾。如果文件不存在,则创建该文件。

🍉返回值🍉

如果文件被成功打开,该函数将返回一个指向🍅file🍅对象的指针,该指针可用于在将来的操作中标识流。

否则,返回一个空指针。在大多数库实现中,🍅errno🍅变量在失败时也被设置为特定于系统的错误代码。

那么在演示 fopen() 打开文件的示例之前,我们先了解下什么是关闭文件。


🍓🍓fclose() 关闭文件🍓🍓

📢​​​ fclose() 函数声明方式如下↓

int fclose ( FILE * stream );

📢关闭文件的方式↓

关闭与流关联的文件并解除关联。

所有与流关联的内部缓冲区都与流分离并刷新:任何未读输出缓冲区的内容都被写入,任何未读输入缓冲区的内容都被丢弃。

即使调用失败,作为参数传递的流将不再与文件或其缓冲区相关联。 

  • 参数→stream

指向🍅FILE🍅对象的指针,该对象指定要关闭的流。


🍑🍑🍑fopen()代码示例🍑🍑🍑

接下来我们来打开一个文件来康康,分别示例 r 和 w 代码示例如下↓

   

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	FILE* pf = fopen("test.txt", "r");
	//文件没在
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	printf("文件存在\n");
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果:此时,文件是存在的。 

那我们把 test.txt 的文件给进行删除,看看此时的运行结果。

当我们把 test.txt 的文件给进行删除,返回的是空指针。

张三:那我们在 🍅fopen()🍅 的 mode 参数 换成 "w" 来试下看下会出现什么样子的情况。

ヾ(^▽^*)))好哒!

先说下,文件在的情况下。实际上文件在的情况下和上面的情况都是一模一样的,这里就不再追述了,自己可以试下。重点讲下文件没在的情况。

此时,我们运行这段代码看看会有什么神奇的事情会发生(●'◡'●)

FILE* pf = fopen("test.txt", "w");//修改下参数即可!

让我们来康康运行结果吧,不要眨眼( •̀ .̫ •́ )✧

张三:什么情况,它竟然文件存在,不对啊我们不是把 test.txt 文件删除了。它怎么会存在呢。这......

"张三同学一看你就是没看我上面写的博客"(⌐■_■),你再去上面我写的参数 🍅mode🍅介绍那里在仔细的看看。

张三:原来如此,那我们赶紧看看那个文件不存在建立了一个新的文件没有。好的(☆-v-) 

从上述截屏当中我们可以发现,它自己雀氏是新建了一个 test.txt 的文件。

张三:这该不会是你偷偷创建的吧(doge),我:🔥🔥🔥

当然,这里还有很多参数🍅mode🍅感兴趣的小伙伴们,可以自己尝试下。这里就不再一一介绍了。不然,有点多,博主手痛不想打这么多的字(๐॔˃̶ᗜ˂̶๐॓)


🍑🍑🍑文件的顺序读写🍑🍑🍑

📢​​​文件的顺序读写:实际上就是怎么样才能把我所写的数据一一的写在文件当中。

这里再次说下🍅"流"🍅的概念,虽说在上面已经说过了。"防止大家忘记,说的就是你张三"

"流":是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为"流"。

 在写代码的时候,我先介绍下🍅文件的顺序读写🍅的函数的一些使用功能。 


🍉fgetc() → 字符输入函数🍉 

⚡️函数的声明方式如下⚡️

int fgetc ( FILE * stream );

从流中获取字符↓

  • 返回指定流的内部文件位置指示器当前指向的字符。然后,内部文件位置指示符被推进到下一个字符。
  • 如果调用时流位于文件结束位置,则函数返回EOF并设置流的文件结束指示符(feof)。
  • 如果发生读错误,该函数返回EOF并设置流的错误指示符(ferror)。

参数↓

  • 指向标识输入流的FILE对象的指针。

返回值↓

  • 如果成功,则返回字符读取(提升为int值)。返回类型为int,以适应特殊值EOF,表示失败。
  • 如果位置指示符位于文件结束位置,函数返回EOF并设置流的EOF指示符(feof)。
  • 如果发生了其他的读取错误,函数也会返回EOF,但会设置它的错误指示符(ferror)。

✨适用于输入流当中✨


🍉fputc() → 字符输出函数🍉 

⚡️函数的声明方式如下⚡️

int fputc ( int character, FILE * stream );

将字符写入流↓

  • 将一个字符写入流并使位置指示器向前移动。字符被写入到流的内部位置指示器所指示的位置,然后该位置指示器自动前进1。

参数↓

  • character:要写的字符的 int 提升。写入时,该值在内部转换为 unsigned char📩
  • stream:指向标识输出流的FILE对象的指针。

返回值↓

  • 如果成功,则返回所写的字符。如果发生写错误,则返回EOF并设置错误指示器(ferror)

✨适用于输出流当中


​​​​​​​🍓🍓 fputc() 代码示例​​​​​​​🍓🍓

那么这里我们就用 fputc() 函数开始写入文件在 test.txt 文件上写入"c"

代码示例如下↓ 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	FILE* pf = fopen("test.txt", "w");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//写文件
	fputc('c', pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

张三同学:那么我们看看是否写入了数据( ̄▽ ̄)"

从上述图中可以看出成功写入了数据🍅'c'🍅,当然我们要注意下即使我们只要以🍅'w'🍅的形式去打开的时候,即使原来文件当中有数据内容也会被清空。

张三:真的是太神奇了(-/*˃̶ᗜ˂̶/*-)

这里注意下是可以进行多个的假设我们要 cpp 的话,直接在加下就可以了。如下代码所示↓

fputc('c', pf);
fputc('p', pf);
fputc('p', pf);

特别注意一点假设我们是这样的↓

fputc('1001', pf);

它这里写入文件数据当中会是都是按照最后一位的数字来的,也就是数字1。

🍉"三个流"🍉 

在这里再说说"流"的概念 FILE*,实际上C语言只要运行起来就默认打开了三个流↓

  1. stdin → 标准输入流 → 键盘
  2. stdout → 标准输入流 → 屏幕
  3. stderr → 标准错误流 → 屏幕

那这不就可以配合我们上面所说的函数去使用了吗。

张三:对啊,毕竟它们实际上也是 FILE* 啊(●'◡'●)

那么,假设我需要用 fputc() 在屏幕上给输出信息,就用 🍅stdout🍅 不就可以了吗。

代码示例如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	FILE* pf = fopen("test.txt", "w");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//写文件
	fputc('c', stdout);
	fputc('p', stdout);
	fputc('p', stdout);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果如下🖊

 

🍑🍑​​​​​​​🍑文件操作函数代码示例🍑🍑​​​​​​​🍑

🍓🍓 fgetc() 代码示例​​​​​​​🍓🍓

那么与之相反的 fgetc() 就适合与输入流,那么我们这次就用"r"进行读文件,本身也是用这个来进行读文件的。代码示例如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	FILE* pf = fopen("test.txt", "r");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//读文件
	for (int i = 0; i < 10; i++)
	{
		int ret = fgetc(pf);
		printf("%c", ret);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

聪明的小伙伴想到了没有,多个直接用循环即可。但是🍅fgetc()🍅必须要包含到循环。 

运行结果🖊

Cyuyanyyds,注意:这里我们的 test.txt 文件是 Cyuyanyyds。

单个直接这样即可(☆-v-),张三你明白了没。张三:属实get到了(ノ*・ω・)ノ

		int ret = fgetc(pf);
		printf("%c", ret);

🍓🍓fputs() 写入"字符串"🍓🍓

当然还有些函数例如下↓

fputs() → 可以写入文件按照一行进行写入"字符串" 。

fputs("Cyuyan\n",pf);
fputs("ttdyyyds\n",pf);

改变下这个以及写入的是写入是 "w" 即可 !( •̀ .̫ •́ )✧

从这里我们可以发现成功了,成功的写入进去了并且是以"字符串"的形式来写入进去的。 

还是写下代码吧。ヾ(≧▽≦*)o

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	char arr[10] = { 0 };
	FILE* pf = fopen("test.txt", "w");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//写文件
	fputs("Cyuyan\n", pf);
	fputs("ttdyyyds\n", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

🍓🍓fgets()从流中获读取"字符串" 🍓🍓

fgets() → 从流中获读取"字符串"。

📢fgets() 函数声明方式如下↓

char * fgets ( char * str, int num, FILE * stream );
  • str→指向一个字符数组的指针,在这个数组中,读取的字符串将被复制。
  • num → 在这里复制到str的最大字符数(包括结束的空字符),这个 num 是要 - 1的。当你输入的是4,那么它的这个实际上只会获取3个字符。
  • stream → 指向标识输入流的FILE对象的指针。Stdin可以作为从标准输入中读取的参数。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	char arr[10] = { 0 };
	FILE* pf = fopen("test.txt", "r");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//读文件
	fgets(arr, 7, pf);
	printf("%s\n",arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果🖊

Cyuyan

从这里我们可以看出只有六个字符,所以我们可以看出 num - 1 的,最多只能读六个要留'\0'的一个位置。 


🍓🍓fprintf()格式化输出函数🍓🍓

📢fprintf() 函数声明方式如下↓

int fprintf ( FILE * stream, const char * format, ... );

将由format指向的C语言字符串写入流。如果format包含格式说明符(以%开头的子序列)

那么format后面的附加参数将被格式化并插入到结果字符串中,替换它们各自的说明符。

format形参之后,函数期望的附加参数至少与format所指定的相同。

  • 参数如下↓

stream→指向标识输出流的FILE对象的指针。

format→包含要写入流的文本的C字符串。它可以选择性地包含嵌入的格式说明符,这些格式说明符将被后续附加参数中指定的值所替换,并按照请求进行格式化。

介绍完 🍅fprintf()🍅让我们来用代码示例讲解下,如何写入一个结构体文件 放在 test.txt 文件上。

代码示例如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct student
{
	char name[20];//学生名字
	char sex[5];//学生性别
	char id[20];//学生学号
	int age;//学生年龄
};
int main(void)
{
	struct student s = { "zhangsan", "nan", "10", 18 };
	FILE* pf = fopen("test.txt", "w");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//写文件
	fprintf(pf,"%s %s %s %d",s.name, s.sex, s.id, s.age);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

从上述截屏当中我们成功的用 🍅fprintf()🍅 进行了格式化输出(*^-^*)

张三:你该不会是自己加上去的吧(doge~),我:🔥🔥🔥

当然,如果你要达到换行的效果也是可以的加在'\n'换行符即可。


🍓🍓fscanf()格式化输入函数🍓🍓

如果你会用上面的格式化输出函数,那么这个函数也就会了。

📢fscanf()​​​​​​​ 函数声明方式如下↓

int fscanf ( FILE * stream, const char * format, ... );

从流中读取格式化数据。

从流中读取数据,并根据参数格式将其存储到附加参数所指向的位置。附加的参数应该指向已经分配的对象,其类型由格式字符串中相应的格式说明符指定。

  • 参数如下↓

stream→指向FILE对象的指针,该对象标识要从中读取数据的输入流。

format→C语言当中的字符串,包含一个字符序列,控制如何处理从流中提取的字符.....

那么现在我们进行读文件。示例代码如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct student
{
	char name[20];//学生名字
	char sex[5];//学生性别
	char id[20];//学生学号
	int age;//学生年龄
};
int main(void)
{ 
	struct student s = {"张三","你好","嗯",886};
	FILE* pf = fopen("test.txt", "r");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//读文件
	fscanf(pf,"%s %s %s %d",s.name,s.sex,s.id,&(s.age));

	//打印
	printf("%s %s %s %d", s.name, s.sex, s.id, s.age);

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

成功写入进去结构体当中的结构体类型的值\^o^/ 

	//读文件
	scanf(stdin,"%s %s %s %d",s.name,s.sex,s.id,&(s.age));

	//打印
	fprintf(stdout,"%s %s %s %d", s.name, s.sex, s.id, s.age);

如果你是这个样子实际上也是没有任何问题的,本质上f无论是输出还是输入都和没有的差不多知识把相对应的东西改下就可以了意思是一样的。

我再把这三个流放在下面怕各位同学还要上去找(o゚v゚)ノ

  • stdin → 标准输入流 → 键盘
  • stdout → 标准输入流 → 屏幕
  • stderr → 标准错误流 → 屏幕

🍑🍑​​​​​​​🍑fread()🍚fwrite()二进制读/写函数🍑🍑​​​​​​​🍑

两个函数是以二进制当中去读取文件的分别是↓

  • 🍅fread🍅 →  二进制输入/读。
  • 🍅fwrite🍅 → 二进制输出/写。 

📢fread()​​​​​​​ 函数声明方式如下↓

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

从流中读取count元素数组,每个元素的大小为size字节,并将它们存储在ptr指定的内存块中。

流的位置指示器被读取的总字节数提前。

如果成功读取的总字节数是(size*count)。

  • 参数如下↓

ptr→指向内存块的指针,该内存块的大小至少为(size*count)字节,转换为void*类型。

size→要读取的每个元素的大小(以字节为单位)。Size_t是一个无符号整型类型。

count→元素的数量,每个元素的大小为字节大小。

stream指向指定输入流的FILE对象的指针。

  • 返回值如下↓

返回成功读取的元素总数。如果这个数字与count参数不同,则在读取时发生了读取错误或到达了文件结束符。在这两种情况下,都设置了合适的指示器,可以分别用ferror和feof检查。
如果size或count为零,则函数返回零,而流状态和ptr指向的内容都保持不变。

fwrite()实际上它两的参数值都一样,这里就不再说了↓

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

不对,还是有不一样的,不知道你发现了没有 doge (o゚v゚)ノ 


🍑🍑​​​​​​​🍑fwrite()🍚fread()代码示例🍑🍑​​​​​​​🍑

fwrite() 代码示例如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct student
{
	char name[20];//学生名字
	char sex[5];//学生性别
	char id[20];//学生学号
	int age;//学生年龄
};
int main(void)
{ 
	struct student s = {"张三","你好","嗯",886};
	FILE* pf = fopen("test.txt", "w");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//写文件(二进制形式写)
	fwrite(&s, sizeof(struct student), 1, pf);

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

成功写入但是从上面图中可以看出会出现乱码,原因实际上就是它是以二进制形式写的。 这里字符串我们能看懂是因为二进制写进去和文本写进去的形式都是一样的。整数或者浮点数写进去的概念完全是不一样的。 

那么我们试试 🍦fread()🍦 到底能不能看的懂(´▽`ʃ♡ƪ)

fread() 代码示例如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct student
{
	char name[20];//学生名字
	char sex[5];//学生性别
	char id[20];//学生学号
	int age;//学生年龄
};
int main(void)
{ 
	struct student s = { 0 };
	FILE* pf = fopen("test.txt", "r");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//读文件
	fread(&s, sizeof(struct student), 1, pf);
	//打印
	printf("%s %s %s %d\n", s.name, s.sex, s.id, s.age);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

从这里我们可以知道我们用 fread() 就 可以读懂了。好耶o(* ̄▽ ̄*)o】

张三:🙅‍好耶~,我:#########@%¥******

🍑🍑​​​​​​​🍑文件的随机读写🍑🍑​​​​​​​🍑 

当然在上述我所说的都是顺序的读写,那么现在我们开始讲解下什么是随机读写。

张三:那个謓泽为什么需要有随机读写呢,我用文件读写它难道不香吗(⊙x⊙;)

我:张三同学出现文件的随机读写绝对是有它的一个好处的,不然为什么它会被出现呢。那么就来和你说说文件随机读写的好处。

好处→速度快,便于进行数据处理。这个就是文件随机读写的好处。

但是,有好处必有坏处。事物都是会具有两面性的。

缺点→占用内存较大。

我:张三同学你明白了没。

张三:GET到了(●'◡'●),但是文件什么是随机读写。

如下图所示↓

🍓🍓fseek() - 重新定位流位置指示器🍓🍓

📢fseek()​​​​​​​​​​​​​​ 函数声明方式如下↓

int fseek ( FILE * stream, long int offset, int origin );
  • 重新定位流位置指示器

将与流关联的位置指示器设置为一个新位置。

对于以二进制模式打开的流,新位置是通过在origin指定的参考位置上添加偏移量来定义的。

对于以文本模式打开的流,offset要么为零,要么为之前调用ftell时返回的值,而origin必须为SEEK_SET。

如果函数调用这些参数的其他值,支持取决于特定的系统和库实现(不可移植)。成功调用文件结束符后,流的文件结束符内部指示器将被清除。

  • 参数功能如下↓

stream→指向标识流的FILE对象的指针。

offset→二进制文件:从原始文件偏移的字节数。

origin→作为偏移量参考的位置。它由<cstdio>中定义的下列常量之一指定,专门用作该函数的参数如下↓

  1. SEEK_SET:开头的文件。
  2. SEEK_CUR:文件指针的当前位置。
  3. SEEK_END:最后的文件。
  • 这里我们用 SEEK_CUR 来举例子如下代码所示↓
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	FILE* pf = fopen("test.txt", "r");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//读取文件
	int ret = fgetc(pf);
	printf("%c", ret);

	//SEEK_CUR→调整文件的指针
	fseek(pf, 2, SEEK_CUR);

	ret = fgetc(pf);
	printf("%c", ret);

	ret = fgetc(pf);
	printf("%c", ret);

	ret = fgetc(pf);
	printf("%c", ret);

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

运行结果🖊 

上述图中不知道你看懂了没有,我画的应该还不错吧(✿◕‿◕✿) 

🍓🍓ftell() - 获取流中的当前位置🍓🍓

📢ftell() 函数声明方式如下↓

long int ftell ( FILE * stream );
  • 获取流中的当前位置

返回流的位置指示器的当前值。

对于二进制流,这是从文件开始的字节数。

对于文本流,数值可能没有意义,但仍然可以使用fseek将位置恢复到相同的位置(如果使用ungetc返回的字符在被读取时仍然挂起,该行为是未定义的)。

  • 参数如下↓

stream→指向标识流的FILE对象的指针。

  • 返回值↓

如果成功,将返回文件指针对于起始位置的偏移量。

失败时,返回-1L,并将errno设置为特定于系统的正值。

  • 示例代码如下↓还是上面的代码为例
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	FILE* pf = fopen("test.txt", "r");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//读取文件
	int ret = fgetc(pf);
	printf("%c", ret);

	//SEEK_CUR→调整文件的指针
	fseek(pf, 2, SEEK_CUR);

	ret = fgetc(pf);
	printf("%c", ret);

	ret = fgetc(pf);
	printf("%c", ret);

	ret = fgetc(pf);
	printf("%c", ret);

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

🍦ftell()🍦  可以告知我们偏移量的值的大小是多少。

拓展→🍅rewind(FILE * stream)🍅能让文件指指针位置回到起始的地址。

🍑🍑​​​​​​​🍑文本文件和二进制文件🍑🍑​​​​​​​🍑 

🍉文本文件🍉

一种计算机文件,它是一种典型的顺序文件,其文件的逻辑结构又属于流式文件。特别的是,文本文件是指以ASCII码方式(也称文本方式)存储的文件,更确切地说,英文、数字等字符存储的是ASCII码,而汉字存储的是机内码。文本文件中除了存储文件有效字符信息(包括能用ASCII码字符表示的回车、换行等信息)外,不能存储其他任何信息。说简单点,文本文件实际上就是把内存文件转换成ASCll码的值,最后存到文件当中去。

🍉二进制文件🍉

包含在 ASCII  及扩展 ASCII 字符中编写的数据或程序指令的文件。计算机的文件基本上分为二种:二进制文件和 ASCII(也称纯文本文件),图形文件及文字处理程序等计算机都属于二进制文件。这些文件含有特殊的格式及计算机代码。ASCII 则是可以用任何文字处理程序阅读的简单文本文件。说简单点,二进制这种文件是把内存这种二进制数据不加任何的转换直接写到文件当中去的。

文本文件这不用说是很简单的,那么我们来用代码演示下二进制文件。假设我们要写入变量num 的数字 1000,让我们来康康。示例代码如下↓

🍓🍓讲解desu🍓🍓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	int num = 1000;
	FILE* pf = fopen("test.txt", "wb");
	//返回值判断
	if (pf == NULL)
	{
		perror("fopen");
		return 1;//返回
	}
	//写入文件
	fwrite(&num, sizeof(int), 1, pf);

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

💥温馨提示💥:使用上面的模式说明符,文件将作为文本文件打开。为了将文件作为二进制文件打开,必须在模式字符串中包含一个"b"字符。这个额外的"b"字符可以被添加到字符串的末尾从而形成以下复合模式。所以,我们这里的 mode 参数是"wb"。

那么让我们一起看下运行结果🖊

那么有人会说为什么这个是二进制啊。当然如果你想知道它是不是二进制很简单。如果你是使用 vs 的编译器的话可以直接↓

 双击(✿◕‿◕✿)

 

那么 1000 是不是上面的这些数字,我们可以算一算。

首先十进制转换成二进制:0000 0000 0000 0000 0000 0011 1110 1000

那么接下来我们转换成十六进制:00 00 03 E8

张三:哇塞学会了(★ ω ★)

不错!那么我们可以知道这个是没有任何的问题的。这种进制转换是必须要拿捏的。

🍑🍑​​​​​​​🍑文件读取结束的判定🍑🍑​​​​​​​🍑

🍉错误使用 feof()🍉 

在文件读取的过程当中,不能使用feof()的函数的返回值直接用来判断文件是否是结束的。

而是在应用当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

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

EOF→通常在文本的最后存在此字符表示资料结束。

二进制文件的读取结束判断,判断返回值是否小于实际当中要读的个数。

  • fread判断返回值是否小于实际要读的个数。

🍓🍓文件缓冲区🍓🍓

文件是指存储在外部存储介质上的、由文件名标识的一组相关信息的集合。由于CPU 与 I/O 设备间速度不匹配。为了缓和 CPU 与 I/O 设备之间速度不匹配矛盾。文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。使用文件缓冲区可减少读取硬盘的次数。​​​​​​​

文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。通过磁盘缓存来实现,磁盘缓存本身并不是一种实际存在的存储介质,它依托于固定磁盘,提供对主存储器存储空间的扩充,即利用主存中的存储空间, 来暂存从磁盘中读出(或写入)的信息。 主存也可以看做是辅存的高速缓存, 因为,辅存中的数据必须复制到主存方能使用;反之,数据也必须先存在主存中,才能输出到辅存。

一个文件的数据可能出现在存储器层次的不同级别中,例如,一个文件数据通常被存储在辅存中(如硬盘),当其需要运行或被访问时,就必须调入主存,也可以暂时存放在主存的中。磁盘高速缓存当中大容量的辅存常常使用磁盘,磁盘数据经常备份到磁带或可移动磁盘组上,以防止硬盘故障时丢失数据。有些系统自动地把老文件数据从辅存转储到海量存储器中,如磁带上,这样做还能降低存储价格。

相信大家对 🍅getchar()🍅 函数不陌生,当我们在键盘当中输入字符并按下回车的时候,我们所输入的数据会被存放在 缓冲区 当中去。那么在这里放到缓冲区的好处是什么?

举个例子,我们知道计算机CPU的处理速度很快的,而我们键盘的输入速度总是比不过CPU的处理速度,那么CPU就得一直等着键盘输入完,这样很浪费资源。于是,我们党键盘输入完了,再让CPU一次性处理,这样就会大大地提高效率。就好比,老师提问问题如果提出一个问题,让一个同学回答 又 或者一次提十个问题,让十个同学来回答,这样绝对是一次提出十个问题的效率会高很多。

又比如,我们的打印机打印文档,打印机的处理速度是很慢的,所以我们会将文档输出到打印机的缓存中去,这样打印机就可以自行慢慢打印,而不必占用CPU资源。


🔥最后🔥 

如果这篇文章有帮助到你的话,不妨三连支持博主下(ง •_•)ง 你的支持就是对我写作的最大的动力,需要交流学习可以添加博主的微信即可。

★最后★点赞👍 关注👋 收藏📑  == 学会✔

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

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

相关文章

MyBatis注解CRUD执行流程剖析

MyBatis Study Notes Day03 结果映射ResultMap 引入resultMap–MyBatis中最强大的元素 数据库字段名&#xff1a;&#xff1a; 实体类字段名&#xff1a; public class User {private int id;private String name;private String password;如上所示&#xff0c;当sql的字段…

动态规划详解(1)——基础概念

动态规划是数学、编程中一个重要的算法动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是运筹学的一个分支&#xff0c;是求解决策过程最优化的过程。20世纪50年代初&#xff0c;美国数学家贝尔曼&#xff08;R.Bellman&#xff09;等人在研究多阶段决策过…

Java——根据身高重建队列

题目链接 leetcode在线oj题——根据身高重建队列 题目描述 假设有打乱顺序的一群人站成一个队列&#xff0c;数组 people 表示队列中一些人的属性&#xff08;不一定按顺序&#xff09;。每个 people[i] [hi, ki] 表示第 i 个人的身高为 hi &#xff0c;前面 正好 有 ki 个…

Spring AOP表达式(execution)规则——排除切点的应用

背景 需要项目原切面的基础上排除一些类中方法。 本篇文章主要介绍了SpringBoot AOP Pointcut切入点表达式&#xff0c;以及如何排除某些类中的方法的方式。 execution(* com.winup.web.controller..*.*(..)) 参数说明 符号含义execution&#xff08;&#xff09;表达式的…

【C++之类和对象】默认成员函数

目录前言一、默认成员函数二、构造函数三、析构函数四、拷贝构造函数五、赋值运算符重载前言 前面我们学习了一些类和对象的基本知识&#xff0c;知道了什么是类&#xff0c;类中包括什么东西&#xff0c;以及能够使用一个类来实例化对象&#xff0c;并且会计算类对象的大小。这…

Java Collection 接口下的 “ List 集合” 与 “ Set 集合 ”

Java Collection接口下的“ List 集合” 与 “ Set 集合 ” 每博一文案 一个人最好的底牌&#xff0c;就这两个字: 靠谱,是最高级的聪明。 师父说&#xff1a;人生一回&#xff0c;道义一场&#xff0c;你对人对事的态度&#xff0c;藏着你一生的福报。 千金难买好人缘&#x…

SpringBoot(三):日志文件

目录一、日志文件1.1 日志文件的作用1.2 Spring Boot内置了日志框架1.3 日志的格式说明1.4 自定义日志打印1.5 日志的持久化1.6 日志的级别1.6.1 日志级别有什么作用1.6.2 日志的级别划分1.6.3 日志级别的设置1.7 使用lombok输出日志1.7.1 lombok的原理1.7.2 lombok其他注解一、…

在JS文件中使用或扩展已有的vue文件

工作中遇到一个给现有项目增加一个超时重新登录的提醒框&#xff08;可在提醒框中直接登录本账户&#xff09;。 由于页面稍微复杂&#xff0c;本人又是脚手架一把梭过来的&#xff0c;对于直接使用 js 来完成一整个复杂还带逻辑的页面稍显吃力&#xff0c;所以决定先写一个 vu…

建模助手【有求必应】的正确打开方式

今天的话题主要想解除大家对[有求必应] 的一些误解。 因为在日常的反馈中用户似乎对于[提需求] 这玩意儿无论是从概念上还是动作上都很不 “熟悉”。 其实我们对软件认知的上限是一个软件功能的上限&#xff0c;产品以及行业的发展都要从打破固有认知开始。 期待更多的你们跳出…

《新华日报·科技周刊》聚焦蓝海彤翔与《流浪地球2》

瞄准世界科技前沿瞄准江苏科技创新瞄准日常科技生活《新华日报科技周刊》第203期聚焦《流浪地球2》中的大国重器其实就在我们身边重点报道了蓝海创意云渲染农场为《流浪地球2》提供了云计算渲染服务的重要成果“数字生命计划”就是元宇宙吗&#xff1f;电影中人类面临末日危机&…

0基础如何入门人工智能?

1.1 概念 根据维基百科的解释&#xff0c;人工智能是被机器展示的智力&#xff0c;与人类和其他动物的自然智能相反&#xff0c;在计算机科学中 AI 研究被定义为 “代理人软件程序”&#xff1a;任何能够感受周围环境并且能最大化它成功机会的设备。 1.2 重大事件 2016 年 3…

[Java]JavaWeb学习笔记(尚硅谷2020旧版)

文章目录&#x1f3c0; 视频及资料地址&#x1f3c0; XML⚽ XML 简介&#x1f3d0; xml 的作用⚽ XML 语法&#x1f3d0; 文档声明&#x1f3d0; 注释&#x1f3d0; 元素(标签)⚾ XML 命名规则⚾ xml 中的元素(标签)也分单双标签&#x1f3d0; xml 元素属性&#x1f3d0; 语法规…

PHP控制反转和依赖注入的理解(通俗易懂)

目录 1.IoC是什么 2.IoC能做什么 3.IoC和DI 4.IoC(控制反转) 5.DI(依赖注入) 6.我对IoC(控制反转)和DI(依赖注入)的理解 学习PHP各个框架的过程中&#xff0c;都会听过IoC(控制反转) 、DI(依赖注入)这两个概念&#xff0c;总觉得IoC 、DI这两个概念是模糊不清的&#xff…

WINSOFT JSEngine Delphi 6-D11

WINSOFT JSEngine Delphi 6-D11 WinsoftJSEngine被认为是一个海豚引擎&#xff0c;包括一个强大的JavaScript引擎。 Winsoft JSEngine的功能和特点&#xff1a; Microsoft ChakraCore JavaScript强大的引擎实用程序 支持32位和64位窗口 提供给海豚版本6 Eli 10.1和Lazarus 为产…

29岁,从餐饮到网络安全,大龄转行逆袭成功

大龄转行&#xff0c;一直在网络上备受讨论。 从学习能力、试错成本来考虑&#xff0c;转行一定是越早越好&#xff0c;而大龄转行风险极大。 大龄转行&#xff0c;固然并非一条绝路&#xff0c;苏老泉&#xff0c;二十七&#xff0c;始发愤&#xff0c;读书籍&#xff0c;有的…

C++学习记录——사 类和对象(1)

文章目录1、面向对象和面向过程的初步理解2、类的引入3、类的定义4、类的访问限定符及封装1、访问限定符2、封装5.类的实例化6、类对象模型7、this1、this指针2、空指针问题3、C语言和C简单对比1、面向对象和面向过程的初步理解 C语言是一个面向过程的语言&#xff0c;C是一个…

美颜sdk人脸识别代码技术分析

很多人问过小编&#xff0c;什么样的美颜sdk才算好&#xff1f;对于这个问题&#xff0c;小编认为至少要符合以下几个特点。 1、稳定性强&#xff1b;2、识别精准&#xff1b;3、功能多样&#xff1b;4、集成容易&#xff1b;5、离线使用&#xff1b;6、支持多端&#xff1b;7、…

新手入门,深入解析 python lambda表达式

lambda 表达式是 Python 中的匿名函数。它接受任意数量的参数&#xff0c;并返回一个单个表达式的值。它的语法格式如下&#xff1a; lambda arguments: expression 文章目录lambda 函数原型解释lambda 函数用作其它参数lambda 函数高级的技巧多个参数返回多个值条件表达式嵌套…

java基础巩固-宇宙第一AiYWM:为了维持生计,编程语言番外篇之机器学习(项目预测模块总结:线性回归算法、逻辑回归分类算法)~整起

机器学习一、机器学习常见算法&#xff08;未完待续...&#xff09;1.算法一&#xff1a;线性回归算法&#xff1a;找一条完美的直线&#xff0c;完美拟合所有的点&#xff0c;使得直线与点的误差最小2.算法二&#xff1a;逻辑回归分类算法3.算法三&#xff1a;贝叶斯分类算法4…

PythonWeb开发基础(一)B/S开发和http协议

文章目录PythonWeb开发基础&#xff08;一&#xff09;B/S开发和http协议请求响应连接PythonWeb开发基础&#xff08;一&#xff09; B/S开发和http协议 推荐书籍&#xff1a;《HTTP权威开发指南》 前端&#xff1a;数据的格式化呈现&#xff0c;python中的format函数其实就…