【C语言】文件操作(2)(文件缓冲区和随机读取函数)

在这里插入图片描述

文章目录

  • 一、文件的随机读取函数
    • 1.fseek函数
    • 2.ftell函数
    • 3.rewind函数
  • 二、文件读取结束的判断
    • 1.被错误使用的feof
    • 2.判断文件读取结束的方法
    • 3.判断文件结束的原因
      • feof
      • ferror
      • 判断文件读取结束原因示例
  • 三、文件缓冲区

一、文件的随机读取函数

   在上一篇的文章中,我们讲到了文件顺序读取的各种函数,顺序读取也就是从开头读到结尾,没有选择,我们今天要讲的就是文件的随机读取
   也就是我们不必按照文件的顺序进行读写,可以通过一些函数更改读写的位置,从而实现我们所说的随机读写,接下来我们就来学习这些函数

1.fseek函数

   fseek函数用来定位文件内容的光标,光标默认在开头,如果读取了一个字符,那么光标就会往后面移动一位,而fseek函数可以通过偏移量来定位光标,然后我们就可以从定位的位置进行读写,我们来看看fseek的原型:

int fseek ( FILE * stream, long int offset, int origin );

   如果函数定位成功,那么就会返回0,定位失败就会返回一个非0值
   它的第一个参数是我们要定位光标的流,第二个参数就是我们的偏移量,是一个长整型,它要根据我们的第三个参数来定,第三个参数origin可以是三个常量值,如下图:
在这里插入图片描述
   当它取SEEK_SET时表示,光标的偏移量要从文件开头开始计算,当它取SEEK_CUR时,光标的偏移量要从当前光标位置开始计算,当取SEEK_END时,光标的偏移量要从文件尾开始计算,我们来举一个例子说明:
   假设有一个文件中存储着abcde,现在光标的位置在a后面,如:

a | bcde

   我们想要获取字符d,那么就要把光标移动到d的前面,如下:

abc | de 

   那么这时我们就要计算偏移量,偏移量是针对第三个参数origin的不同取值的,当origin取SEEK_SET时,我们光标的偏移量要从文件开头开始计算,那么此时我们要把光标移动到d前面,偏移量就是3
   当origin取SEEK_CUR时,光标的偏移量就要从当前光标位置开始计算,那么此时我们要把光标移动到d前面,偏移量就是2
   当origin取SEEK_END时,光标的偏移量要从文件尾开始计算,那么此时我们要把光标移动到d前面,偏移量就是-2
   所以偏移量不是绝对的,要看fseek第三个参数的取值
   接下来我们就来看一段代码,尝试分析代码运行的结果:

#include <stdio.h>int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}fputs("This is an apple.", pf);fseek(pf, 9, SEEK_SET);fputs(" orange", pf);fclose(pf);pf = NULL;return 0;
}

   首先程序以写的方式打开了当前目录下的文本文件test.txt,然后写入了一句英文:

This is an apple.

   然后对文件里的光标位置做了更改,它的含义就是将光标移动到从文件开头计算,偏移量为9的位置,我们经过计算,应该在以下这个位置:

This is a|n apple.

   光标在a和n的中间,那么这时我们又进行了写入,写入了如下字符串:

 book

   要注意的是,book的前面有一个空格,所以我们写入时不要把这个空格忘记了,使用w写的时候,会覆盖之前的数据,所以空格会覆盖n,book会覆盖 app,所以写入之后,应该是这个样子的字符串:

This is a bookle.

   我们来运行一下这个程序,然后去看看我们的test.txt的内容和我们的预期是否相同,如下:
在这里插入图片描述
   可以看到代码的结果正如我们所料,但是我们还是有一些疑问,我们难道每一次都去数偏移量吗?有没有什么办法可以计算偏移量呢?就要看我们接下来要学习的函数:ftell了

2.ftell函数

   ftell函数的作用就是返回当前文件光标到文件开头的偏移量,我们来看看它的原型:

long int ftell ( FILE * stream );

   它的原型看起来也很好理解,参数就是我们要操作的流,返回值是长整型,返回的就是当前文件光标到文件开头的偏移量
   接下来我们直接来看例子,看看代码运行会发生什么:
   代码的前提是,当前目录下有一个test.txt文件,里面的内容是hello world!

#include <stdio.h>
int main()
{long size;FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}fseek(pf, 0, SEEK_END); size = ftell(pf);fclose(pf);printf("Size of test.txt: %ld bytes.\n", size);pf = NULL;return 0;
}

   首先程序打开了文件test.txt,创建了一个长整型变量size,随后使用了fseek函数,我们要看得懂这句代码是什么意思,它的意思就是,将文件光标移动到离文件结尾偏移量为0的地方,实际上就是把光标移动到了文件末尾
   然后此时我们使用ftell函数算出文件开头到光标的偏移量,也就是文件开头到文件末尾的偏移量,那么算出来的将会是我们字符的个数,而一个字符占用一个字节,所以我们就间接算出来了文件内容的大小
   我们来看看代码运行结果:
在这里插入图片描述

3.rewind函数

   rewind函数的作用就比较简单了,就是把文件中的指针位置重置到文件开头,我们来看看它的原型:

void rewind ( FILE * stream );

   它的参数就是我们要操作的流,没有返回值,从原型看就可以发现它应该是一个很简单的函数,它的作用就是将文件光标移动到开头,然后我们可以重新在开头对文件进行读写
接下来我们直接上案例,来看看代码运行结果:

#include <stdio.h>
int main()
{int i;char buffer[27];FILE* pf = fopen("test.txt", "w+");if (pf == NULL){perror("fopen");return 1;}for (i = 'A'; i <= 'Z'; i++){fputc(n, pf);}rewind(pf);fread(buffer, 1, 26, pf);fclose(pf);pf = NULL;buffer[26] = '\0';printf(buffer);return 0;
}

   首先,程序以读写的方式打开了当前目录下的test.txt文件,然后将大写字母A到Z的字符写入到了我们的test.txt文件中,随后就到了我们的rewind函数,它直接就将我们的光标移动到了开头
   然后我们就又使用了fread函数将pf中的数据读了出来,然后关闭文件,打印了读出的数据
   现在唯一的问题是,我们之前讲的fread是对二进制文件进行操作,那么它能不能对普通文本文件进行操作呢?我们来看看代码的运行结果:
在这里插入图片描述

   可以看到代码成功把文件中的内容读出来了,说明fread既可以读取二进制文件和文本文件,这是为什么呢?我们可以在cplusplus.com这个链接下搜索这个函数,看看这个函数是如何解释的:
在这里插入图片描述
   可以看到fread是一个函数,它的原型我们也解释过,这里不多说了,我们可以看下一行加粗的字体,翻译过来就是,从流中读取数据块,看到这个解释我们就知道了,它读取时不是 只能读取二进制,而是可以读取数据块
   所以在传参时我们才要传元素个数和元素大小,而读取数据块就不会分它是文本文件还是二进制文件,函数也没有明确说只能读取二进制文件,只是它可以读取二进制文件而已
   而另一个函数fwrite和函数fread也是一样的,它既可以写入文本数据又可以写入二进制数据,因为它写入的时候也是按照数据块进行写入

二、文件读取结束的判断

1.被错误使用的feof

   牢记:在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的读取是否结束,feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束
   再通俗一点的说,feof使用的前提就是文件的读取已经结束了,它的作用就是在文件读取结束后判断文件是不是读到末尾结束,如果我们用它去判断文件读取是否结束,很明显是错误的

2.判断文件读取结束的方法

   所以我们对不同的文件,提供了不同的判断方法,如下:
(1)文本文件是否读取结束
   根据我们的读取函数的返回值来确定,在上一篇文章中我们就学过了文件读取函数,这里我们就不再多赘述,如果忘记可以翻看上一篇文章:【C语言】文件操作(1)(文件打开关闭和顺序读写函数的万字笔记)

  • 判断函数fgetc的返回值是否为EOF
  • 判断函数fgets的返回值是否为NULL

(2)二进制文件是否读取结束

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

3.判断文件结束的原因

   刚刚我们学习了如何判断文件读取结束,那么文件读取结束了不一定就是正常的全部读取成功了,所以又会有正常读取结束和错误读取结束两种区别,正常读取结束就是文件读取到了文件末尾,错误读取就是因为某种原因读取出现错误了,没有读到文件末尾
   那么我们怎么判断文件是正常读取结束还是错误读取结束了呢?一般是使用feof函数和ferror函数来进行判断

feof

   feof函数我们在上面已经做了基本介绍,它的作用就是,在文件读取结束后,判断文件读取结束的原因是不是碰到了文件尾,我们来看看它的原型:

int feof ( FILE * stream );

   函数的参数是要操作的流,当文件是正常读取结束,也就是文件是因为读到末尾了而结束,就返回一个非0值,非正常读取结束就返回0

ferror

   ferror函数就是在文件读取结束后,用来判断文件是否是错误读取结束,和feof有点相似,只是判断的内容不同,我们来看看它的原型:

int ferror ( FILE * stream );

   它的参数也是要操作的流,如果文件是错误读取结束,那么就返回非0值,如果没有错误读取结束,也就是正常读取结束了,就返回0

判断文件读取结束原因示例

   我们刚刚学习了feof和ferror函数,现在我们就来使用它们来判断文件结束的原因,要注意一个前提:当前目录下有一个文件test.txt,里面的内容是hello world!,接下来我们来看看怎么把这两个函数运用在实战上:

#include <stdio.h>int main()
{char arr[20];FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}fgets(arr, 20, pf);printf("%s\n", arr);if (feof(pf)){printf("文件正常读取结束\n");}if (ferror(pf)){printf("文件错误读取结束\n");perror("读取失败原因");}fclose(pf);pf = NULL;return 0;
}

   我们将读取到的字符串放在了arr中,然后我们来判断文件是否正常读取结束,如果正常读取结束就打印一下这句话,如果错误读取结束,那么就使用perror来打印一下读取失败的原因,最后我们来看看代码运行结果:
在这里插入图片描述

三、文件缓冲区

   当我们对文件写入数据后,如果程序还在进行,并且没有关闭文件,那么我们会发现,我们写入的内容居然没有立刻就出现在文件中,而一旦关闭文件后写入的内容才出现在文件中,这是为什么呢?
   这时我们就要引入文件缓冲区的概念了,ANSIC 标准采⽤“缓冲⽂件系统” 处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”
   从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区,充满缓冲区后再从缓冲区逐个地将数据送到程序数据区(程序变量等)
   在文件中,有多种情况可以刷新缓冲区,将缓冲区的数据写入文件,我们这里就讲一下常用的三种情况

  1. 当缓冲区被装满后自动刷新缓冲区,将数据写入文件
  2. 当文件被关闭时,也会刷新缓冲区,将数据写入文件
  3. 使用fflush函数刷新缓冲区,它可以几乎不受限制的随时刷新缓冲区,使得缓冲区中的数据写入文件

   那么缓冲区具体有多大呢?这个是不确定的,要看编译器的具体实现

   今天的内容就分享到这里啦,也是终于把文件操作写完了,文件操作还是挺难的,所以如果有什么问题欢迎在评论区留言或者私信我
   bye~

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

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

相关文章

Android10 recent键相关总结

目录 初始化流程 点击Recent键流程 RecentsActivity 显示流程 RecentsModel 获取数据管理类 RecentsActivity 布局 已处于Recent界面时 点击recent 空白区域 点击返回键 recent组件配置 Android10 Recent 功能由 System UI&#xff0c;Launcher共同实现。 初始化流程 …

如何克隆Git仓库的子目录:稀疏检出

一、环境 Git 2.34.1 二、前言 一般来说&#xff0c;我们在克隆git仓库的时候&#xff0c;都是一整个仓库都克隆出来的。如果假设现在有一个很大的仓库&#xff0c;仓库里有多个子项目&#xff0c;而我们只想克隆其中一个子项目的时候&#xff0c;应该怎么做呢&#xff1f; …

【Java后端】之 ThreadLocal 详解

想象一下&#xff0c;你有一个工具箱&#xff0c;里面放着各种工具。在多人共用这个工具箱的时候&#xff0c;很容易出现混乱&#xff0c;比如有人拿走了你的锤子&#xff0c;或者你找不到合适的螺丝刀。为了避免这种情况&#xff0c;最好的办法就是每个人都有自己独立的工具箱…

初识适配器模式

适配器模式 引入 生活中的例子&#xff1a;当我们使用手机充电时&#xff0c;充电器起到了转换器的作用&#xff0c;它将家用的220伏特电压转换成适合手机充电的5伏特电压。 适配器模式的三种类型 命名原则&#xff1a;适配器的命名应基于资源如何传递给适配器来进行。 类适配…

第14篇:下一代网络与新兴技术

目录 引言 14.1 下一代网络&#xff08;NGN&#xff09;的定义与特点 14.2 IPv6协议的改进与未来应用 14.3 软件定义网络&#xff08;SDN&#xff09; 14.4 网络功能虚拟化&#xff08;NFV&#xff09; 14.5 量子通信网络 14.6 软件定义广域网&#xff08;SD-WAN&#x…

xlsx xlsx-style-vite 实现前端根据element 表格导出excel且定制化样式 背景 列宽等

前言 先看下最终效果图吧&#xff0c;需要的可以参考我的实现方式 这是最终导出的表格文件 类似这种的&#xff0c;特定单元格需要额外标注&#xff0c;表头也有月份然后细分的&#xff0c;表格组件是这样的 注意 别使用xlsx-style 这个库&#xff0c;太多问题了&#xff0c;…

【C语言刷力扣】1768.交替合并字符串

题目&#xff1a; 解题思路&#xff1a; 将 word1 和 word2 元素依次添加至 ans 的后面。 时间复杂度&#xff1a; &#xff0c; n是word1的长度 m是word2的长度 空间复杂度&#xff1a; char* mergeAlternately(char* word1, char* word2) {int len1 strlen(word1);in…

【Linux】top命令查看CPU、内存使用率、解释

1. top 命令 top 是最常用的实时监控工具之一&#xff0c;可以显示 CPU 的总利用率以及各个进程的 CPU 使用情况。在Linux命令行直接输入top即可查看动态原始数据 top 在 top 命令的输出中&#xff0c;最上面的一行会显示 CPU 的使用情况&#xff1a; us&#xff08;User&a…

图片怎么转文字?11种好用的方法!

如何快速将图片的文字提取出来&#xff0c;可以大量节省手打的时间&#xff0c;无论是截图&#xff0c;或者批量提取照片文字&#xff0c;都经常需要这个操作&#xff01; 作为一名社畜&#xff0c;俺也经常用到各种图片转文字工具&#xff0c;今天通过测评12个主流的图片转文…

面对AI算力需求激增,如何守护数据中心机房安全?

随着人工智能&#xff08;AI&#xff09;技术飞速发展&#xff0c;AI算力需求呈现爆发式增长&#xff0c;导致对数据设备电力的需求指数级攀升。这给数据中心带来前所未有的挑战和机遇&#xff0c;从提供稳定的电力供应、优化高密度的部署&#xff0c;到数据安全的隐私保护&…

OpenVLA-首个开源视觉语言动作大模型

官网&#xff1a;https://openvla.github.io/ 现在大模型已经卷到了机器人领域。 在视觉语言模型&#xff08;VLM&#xff09;的基础上&#xff0c; 加入机器人的动作&#xff08;Action) 这一模态&#xff0c; 视觉语言动作大模型&#xff08;VLA&#xff09;是目前大模型应用…

2024新手攻略:项目管理工具+PMP备考经验不容错过!

&#xff08;一&#xff09;热门工具大盘点 禅道是一款专注于软件开发项目管理的工具。它涵盖了项目管理的各个环节&#xff0c;包括需求管理、任务分配、缺陷跟踪等。禅道的优势在于其对软件开发流程的深入理解和支持&#xff0c;能够帮助开发团队更好地管理项目进度和质量。…

如何应对 Android 面试官 -> ANR 如何优化?线上 ANR 如何监控?

前言 本章主要围绕 ANR 如何监控以及优化&#xff1b; 基本概念 ANR(Android Not Responding) 是指应用程序未响应&#xff0c;Android 系统对于一些事件需要在一定的时间范围内完成&#xff0c;如果超过预订时间未能得到有效响应或者响应时间过长&#xff0c;都会造成 ANR。 …

91 VRRP负载局衡+主备切块 V2

实操四 负载均衡主备 VRRP&#xff08;虚拟路由冗余协议&#xff09;是一种网络协议&#xff0c;用于提高网络的可用性和冗余性。VRRP可以将多个路由设备组成一个虚拟路由器&#xff0c;当主路由设备故障时&#xff0c;自动切换到备用路由设备&#xff0c;从而实现高可用性。 …

Mamba学习笔记(2)—序列数据处理基础

文章目录 (1) RNN&#xff08;Recurrent Neural Networks&#xff09;基本原理代码定义 (2) SLTM (Long Short-Term Memory)基本原理代码定义 (3) GRU (Gated Recurrent Unit)基本原理代码定义 (4) Transformer&#xff08;☆☆☆Attention Is All You Need☆☆☆&#xff09;0…

nginx解决非人类使用http打开的443,解决网安漏扫时误扫443端口带来的问题

一、问题描述 正常访问https的站点时&#xff0c;使用网址https://www.baidu.com&#xff0c;但会有一种错误的访问请求http://www.baidu.com:443&#xff0c;一般都是非人类所为&#xff0c;如漏洞扫描工具&#xff0c;那么请求以后带来的后果是个错误页面 400 Bad Request T…

如何选择合适业务协作平台?—— 板栗看板给你答案

在当今快速发展的商业环境中&#xff0c;团队协作和项目管理变得越来越重要。业务协作平台作为一种工具&#xff0c;可以帮助团队成员进行有效的沟通、任务分配和进度跟踪。这些平台通常具备项目管理、文档共享、实时通讯等功能&#xff0c;以提高团队的工作效率和协作能力。 一…

AttributeError: ‘function‘ object has no attribute ‘decode‘

h5py版本过高&#xff0c;降低版本即可。 1.卸载h5py pip uninstall h5py 卸载后&#xff0c;查看h5py版本&#xff0c;显示这个代表卸载成功。 import h5py print(h5py.__version__) 2. 安装低版本的h5py<3.0.0 目前的版本有&#xff1a; 2.2.1, 2.3.0b1, 2.3.0, 2.3.1,…

如何看ip属于什么地址

在数字化时代&#xff0c;IP地址作为互联网通信的基石&#xff0c;扮演着至关重要的角色。无论是网络管理、安全防护&#xff0c;还是日常的网络访问&#xff0c;理解IP地址的性质和分类都是必不可少的技能。本文将深入探讨如何判断一个IP地址属于哪一类地址&#xff0c;并详细…

阿里云验证码短信发送服务搭建(flask)

参考&#xff1a;https://next.api.aliyun.com/api-tools/sdk/Dysmsapi?version2017-05-25&languagejava-async-tea&tabprimer-doc 我们需要思考验证服务一些要求&#xff1a; 1.验证码只能被验证一次&#xff0c;所以需要状态字段 2.验证码有失效时间&#xff0c;超…