目录
✨前言✨:
🎈一、文本文件与二进制文件🎈:
🎁二、文件读取结束的判定🎁:
1.错误使用 feof 函数:
2.判断文件读取结束:
🎊三、文件缓冲区🎊:
🧧总结🧧:
🛰️博客主页:✈️銮同学的干货分享基地
🛰️欢迎关注:👍点赞🙌收藏✍️留言
🛰️系列专栏:💐【进阶】C语言学习
🧧 C语言学习
🛰️代码仓库:🎉VS2022_C语言仓库
家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!
关注我,关注我,关注我,你们将会看到更多的优质内容!!
🏡🏡 本文重点 🏡🏡:
🚅文本与二进制文件🚃文件读取结束判定🚃文件缓冲区🚏🚏
✨前言✨:
在上节课中我们已经学习了有关本地磁盘文件的常用文件操作,已经能够对本地文件进行调用与读写操作,能够将数据写入本地磁盘,也能够从本地调用数据。同时在上节课中我们也提到过,在我们的磁盘内还存在着一些二进制文件,于是我们今天就再来研究一下文本文件与二进制文件操作的细节。
🎈一、文本文件与二进制文件🎈:
根据数据的组织形式,我们将数据文件称为文本文件或二进制文件。
我们之前在研究各种量的时候就提到过,在我们的计算机内存中,各种数据都是以二进制码的形式进行存储的,而如果这时数据不加转换就向外输出至外存,就是二进制文件:
如此便是二进制文件,而如果我们想要在外存上以 ASCII 码的形式存储数据,就需要在存储前将数据进行转换。而这种以 ASCII 字符形式进行存储的文件,就是文本文件。
那么数据在内存中数据到底是如何让进行存储的呢?
实际上,字符在内存中的存储一律是以 ASCII 码的形式进行存储的,而数值型数据既可以用ASCII 码存储,也可以用二进制形式进行存储。
例如十进制数字 10000 在进行存储时,就可以有两种存储形式:
★ 二进制形式:
00000000 00000000 00100111 00010000
★ ASCII码形式:
00110001 00110000 00110000 00110000 000110000 1 0 0 0 0
我们还是来看实例:
int main()
{
int a = 10000;
FILE* p = fopen("test.txt", "wb");
//“wb”表示以只写模式打开二进制文件
if (p == NULL)
{
perror("FileOpen");
return 0;
}
fwrite(&a, 4, 1, p);
//将变量a中的数据,每四个字节存储一次,写入文件指针p所指向的文件
fclose(p);
p = NULL;
return 0;
}
在上面这段代码运行成功后,我们已经成功的将变量 a 中的数据写入到了本地磁盘对应的 txt 文件中了,可是我们发现,当我们尝试打开本地文件查看存储的数据时,里面看起来并不是我们想要的结果:
难道是我们的程序哪里出错了吗?其实并不是,而是因为我们使用了" wb "方式,即以二进制形式进行了写入,所以我们在尝试直接查看时显示的是二进制符号。
所以我们就需要换一种方式才能对我们的文件内容进行查看。首先选中我们的“ 源文件 ”栏,右击选择“ 添加 ” → “ 添加现有项 ”,将写入时创建的 txt 文件添加进来:
然后选中 txt 文件,右击选择“ 打开方式 ”选项:
接着在选项框中下拉选择“ 二进制编辑器 ”:
这时我们就看到了文件内的实际存储情况了:
它显示我们的文件中的实际存储数据为 10 27 00 00,这又是怎么来的呢?这是因为,我们在存储时根据语句:
fwrite(&a, 4, 1, p);
根据这条语句,我们在进行数据写入时,是四个字节四个字节进行写入的,于是根据其二进制码可以得到它的存储为:
每四个字节进行划分:
0000 0000 0000 0000 0010 0111 0001 0000
于是得到:
00 00 27 10
又因为在之前我们就验证过我的计算机采用的是小端存储模式,于是在进行压栈时会将数据进行倒置存储,于是就有:
10 27 00 00
至此,就得到了我们写入文件中的实际数据内容。
🎁二、文件读取结束的判定🎁:
1.错误使用 feof 函数:
在一段时间的交流中,我发现有很多小伙伴们都错误的使用了 feof 函数,将其用于判断文件是否读取结束,而这种使用方式是错误的。
各位小伙伴们一定要牢记:
★ 在文件的读取过程中,不能通过 feof 函数的返回值来判定文件是否读取结束。
★ 该函数的作用为,在已经确定文件读取结束的情况下,用于判定文件读取结束的原因。
2.判断文件读取结束:
那么我们该如何判断文件是否读取结束了呢?
★ 文本文件判断文件读取结束方法:
①. 使用 fgetc 函数判断是否为 EOF。
②. 使用 fgets 函数判断返回值是否为 NULL。
例如:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int c;
//注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if (!fp)
{
perror("File opening failed");
return 1;
}
// 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);
fp = NULL;
return 0;
}
★ 二进制文件判断文件读取结束方法:
使用 fread 函数判断返回值是否小于实际要读取的数据个数。
例如:
#include<stdio.h>
enum {
SIZE = 5
};
int main(void)
{
double a[SIZE] = { 1.,2.,3.,4.,5. };
FILE* fp = fopen("test.bin", "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);
fp = NULL;
return 0;
}
🎊三、文件缓冲区🎊:
文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。通过磁盘缓存 来实现,磁盘缓存本身并不是一种实际存在的存储介质,它依托于固定磁盘,提供对主存储器存储空间的扩充,即利用主存中的存储空间, 来暂存从磁盘中读出 (或写入)的信息。
例如在国际 ANSIC 标准中,就是采用“ 缓冲文件系统 ”来对数据文件进行处理的。缓冲文件系统会自动地在我们的内存空间中为程序中的每个正在使用的文件开辟一块“ 文件缓冲区 ”。而从我们的计算机内存中向本地磁盘中输出数据时,将会先送至缓冲区,将缓冲区全部装满后一并送达磁盘。若是从磁盘向计算机读入数据,则会从磁盘文件中读取数据,并将其输入至缓冲区,并在充满缓冲区后再逐个地将数据送达程序数据区:
而至于缓冲区的大小,则是由 C 编译系统决定的。
验证缓冲区:
#include <stdio.h>
#include <Windows.h>
//VS2022 WIN10环境测试
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);
//先将代码放在输出缓冲区
printf("睡眠10秒\n");
//已经写数据了,但是打开test.txt文件,发现文件没有内容
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);
//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒\n");
//此时,再次打开test.txt文件,文件有内容了
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
所以在这里,各位小伙伴们一定要注意了,因为缓冲区的存在,数据只有在填满缓冲区后才会进行真正的写入或读取,所以在 C 语言程序代码的编写过程中,在对文件进行操作时,需要刷新缓冲区并在文件操作结束时关闭文件,否则就可能导致文件的读写操作出现问题。
🧧总结🧧:
经过今天内容的补充,我们关于文件操作与管理的知识就全部学完了,不知道各位小伙伴们究竟又掌握了多少呢?这一部分的内容在我们还没有学习数据库的现阶段来说较为实用,可以帮助我们调用和读写本地文件,能够帮助我们将程序运行中产生的有用的数据保存至本地,也能大批量的从本地对数据进行导入。希望各位小伙伴们在闲暇时间中能够多加练习,夯实基础,为后来的学习做好坚实的铺垫,为了明天成为更好的自己埋下基石!
🔥🔥一生中你唯一需要回头的时候,是为了看自己到底走了多远🔥🔥
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!