速通C语言第十二站 文件操作

系列文章目录

 速通C语言系列

 速通C语言第一站 一篇博客带你初识C语言        http://t.csdn.cn/N57xl

 速通C语言第二站 一篇博客带你搞定分支循环   http://t.csdn.cn/Uwn7W

 速通C语言第三站  一篇博客带你搞定函数        http://t.csdn.cn/bfrUM

速通C语言第四站  一篇博客带你学会数组          http://t.csdn.cn/Ol3lz

 速通C语言第五站 一篇博客带你详解操作符      http://t.csdn.cn/OOUBr

速通C语言第六站 一篇博客带你掌握指针初阶   http://t.csdn.cn/7ykR0

速通C语言第七站 一篇博客带你掌握数据的存储 http://t.csdn.cn/qkerU

 速通C语言第八站 一篇博客带你掌握指针进阶    http://t.csdn.cn/m95FK

 速通C语言第八.五站 指针进阶题目练习           http://t.csdn.cn/wWC2x

速通C语言第九站  字符相关函数及内存函数    http://t.csdn.cn/YyBBM

 速通C语言第十站  自定义类型                          http://t.csdn.cn/jsGJ7

速通C语言第十一站  动态内存开辟                  http://t.csdnimg.cn/necjp

感谢佬们支持!首先祝大家元旦快乐!


文章目录

  • 系列文章目录
  • 前言
  • 1、为什么使用文件
  • 2、什么是文件
  •          文件名
  • 3、文件的打开和关闭
  • 4、文件的顺序读写
  •        流
  •        一次读一个
  •        一次读一行
  •        格式化输入输出函数
  •        二进制输入输出函数
  •        综合对比
  • 5、文件的随机读写
  • 6、文本文件和二进制文件
  • 7、文件读取结束的判定
  • 8、文件缓冲区
  • 总结

前言

   在学习之前,我家首先要知道,文件/文件系统是一个很重要的东西,而且研究文件只停留在语言层面是非常片面的。等我们学到操作系统后,才会对此有一个更深的理解.


1、为什么使用文件

比如说我们写一个通讯录,运行之后进行增删查改,但是退出程序后我们的数据就没了

要想有信息拷贝,就要把数据写到文件中。


2、什么是文件

文件分为两种,一种为数据文件,另一种为程序文件(内存文件)

程序文件分为3种 :1、我们写的.c文件

                                2、编译过程中产生的临时文件 .i ,.s .o

                                3、最后生成的可执行程序 .exe

而数据文件简单来说就是我们能从这个文件中读点数据,也能将程序中的文件写到文件中


文件名

    一个文件需要一个唯一的文件标识符,在我们看来是文件名(但是在操作系统看来不是)

文件名由三部分构成

 文件路径+文件名+后缀(注:在Linux系统中文件后缀没用)

例:

C:\code\test.txt


而文件的路径分为两种

一种叫绝对路径,表示从根目录一直到你当前文件的路径

例如/c/Users/86138/tDesktop/test.c 

还有一种叫相对路径,可以表示当前路径或上级路径的文件

比如test.c  ../test.c


3、文件的打开和关闭

每一个被打开的文件都在内存中用一个结构体维护,用于存放文件的部分信息(如文件存放的位置,文件的状态信息等),这个结构体被typedef为FILE

所以我们要想对这个文件做什么,就要用文件指针FILE*调用

关闭一个文件,我们用fclose函数

如果关闭成功,返回0,失败返回EOF


打开一个文件,我们用fopen函数

其中,第一个参数为文件的名字,第二个参数为打开文件的方式,有6种选项选择(我们重点研究前三个)

如果打开成功,返回一个文件指针,失败就返回NULL

我们写一波代码给大家看一下

我们先以 写 打开一个文件,用“w"选项,

意为,如果文件存在,会先清空再打开,如果不存在,会创建出来

FILE* pf = fopen("test.txt", "w");if (pf == NULL){printf("打开失败\n");}//写文件//关文件fclose(pf);pf = NULL;

运行之后打开代码所在的目录

(果然多了一个test.txt)


写了之后我们在读一个文件

FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen\n");}

读和写不同,如果没有该文件,会直接报错(使用perror函数)



4、文件的顺序读写

我们先来补充一个流的概念,流的概念很抽象,我们在没学操作系统之前,只能稍微理解一下

众所周知,我们储存一个程序文件有很多硬件来选择,比如屏幕(屏幕显示文件的本质也是存储),硬盘,U盘,网络

所以选择很多,不同选择的读写方式肯定不同,而在软件层面要考虑所有选择的读写方式显然太SB了,所以我们抽象一个“流”的概念出来,其类型为FILE*

我们在写文件的时候向流中写,剩下向右边的东西怎么写是操作系统的事,他会以多态的方式实现一系列方法,这些都是以后再学的啦


另外,当C语言程序运行起来,就默认打开了3个流

0   stdin (标准输入流)   对应得硬件是键盘

1   stdout(标准输出流)  对应得硬件是屏幕

2   stderr(标准输出错误流)  对应得硬件也是屏幕


一次读一个

我们用fputc/fgetc可以从指定流中输出/输入一个数据

我们尝试向标准输出stdout(显示器)输出3个字母

fputc('z', stdout);fputc('y', stdout);fputc('g', stdout);


再尝试从标准输入stdin中读几个字符

既然我们能从stdout,stdin里读写,那么文件也可以

创建一个test.txt

FILE* pf = fopen("test.txt", "w");//写fputc('a', pf);fputc('b', pf);fputc('c', pf);

 查看一下test.txt

 (成功)

再读

FILE* pf = fopen("test.txt", "r");int ret = fgetc(pf);printf("%c\n", ret);

第二次读


一次读一行

一次读写一个字符,太慢了

由此我们提供fgets读一行,fputs写一行

 

注意:num代表读取的最大个数,如果num=100,则实际读99个,因为要预留一个\0的位置

(因为无论如何这都是在语言的层面读写,就要遵守语言的规则以\0结尾)

另外,如果某一行的大小+1(\0的大小)<num,那就只读这一行,不读下一行

例:

FILE* pf = fopen("test.txt", "w");fputs("abc\n", pf);fputs("qwe\n", pf);fclose(pf);pf = NULL;

打开文件

FILE* pf = fopen("test.txt", "r");char arr[10] = { 0 };fgets(arr, 4, pf);printf("%s\n", arr);

先读4个,发现只读了第一行

fgets(arr, 5, pf);printf("%s\n", arr);

 改成读5个,他依然只读第一行


格式化输入输出函数

fprintf

fscanf

const char* format是我要输的格式,就和我们学的printf,scanf一样

例:

struct S{char arr[10];int num;float sc;};struct S s = { "abcdef",10,5.5};FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");}fprintf(pf, "%s %d %f",s.arr,s.num,s.sc);


二进制输入输出函数

fread

从流中读count个size大小的数据到buffer中

fwrite

把buffer中count个size大小的数据写入buffer中

例:

struct S{char arr[10];int num;float sc;};struct S s = { "abcdef",10,5.5 };FILE* pf = fopen("test.txt", "wb");//二进制写,wbif (pf == NULL){perror("fopen\n");}fwrite(&s, sizeof(struct S), 1, pf);

字符串从二进制写进去还是一样,但是数字不行,我们看不懂

我们看不懂,但是fread可以

struct S s = {0 };FILE* pf = fopen("test.txt", "rb");//二进制读,rbfread(&s, sizeof(struct S), 1, pf);printf("%s %d %f", &s.arr, s.num, s.sc);

读一下

struct S{char arr[10];int num;float sc;};struct S s = { 0};FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");}fscanf(pf, "%s %d %f",s.arr,&(s.num),&(s.sc));//记得加&printf("%s %d %f", s.arr, s.num, s.sc);


综合对比

我们再补充一个sscanf和sprintf,

 从一个字符串中读取一个格式化的数据,其中,format代表格式

再加上我们熟悉的scanf和fscanf

scanf,从标准输入stdin(键盘)输入格式化的语句

针对所有输出流的格式化输入语句,其中输出流包括stdin和文件,也就是说scanf是fscanf的子集

	fscanf(0,const char* format...)//等价于scanf

 把一个字符串中转换一个格式化的数据,其中,format代表格式

printf,针对标准输出stdout(显示器) 的格式化输出语句

 

 fprintf,针对所有输出流 的格式化输出语句,同上,标准输出流包括stdout和文件

		fprintf(1, const char* format...)//等价于printf

5、文件的随机读写

随机读写意为想读哪读哪

之前我们学的顺序读写只能从开头往下一个一个读,有了随机读写,我们可以从任意地方开始读。

第二个参数表示偏移量

第三个参数表示起始位置,有三种选择

SEEK_CUR 当前文件指针的位置

SEEK_END 文件末,此时偏移量只能为负,即从后向前读(但是其封装的系统调用lseek中为可正可负,会对文件相应扩容,并产生文件空洞等,此处可以忽略)

SEEK_SET  文件开始,此时偏移量只能为正,即从头开始读

我们写个代码来用一下这个函数

先将我们的test.txt中写上abcdef

FILE* pf = fopen("test.txt", "r");int ch = fgetc(pf);printf("%c\n", ch);//ach = fgetc(pf);printf("%c\n", ch);//bch = fgetc(pf);printf("%c\n", ch);//c

显然,再往下读就是d了,现在我们又想读b了,怎么办?调整一下文件指针,往前推两个

fseek(pf, -2, SEEK_CUR);ch = fgetc(pf);printf("%c\n", ch);

(又读到b了,牛逼)


与之补充的还有两个函数

ftell

可以返回我们当前文件的偏移量

例:

(我们当前读完了b,要读c,所以偏移量为2)


rewind

让文件指针的位置又回到文件开始

例:

printf("文件的偏移量为:%d\n", ftell(pf));rewind(pf);printf("文件的偏移量为:%d\n", ftell(pf));


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

根据数据得组织形式,数据文件被称为文本文件/二进制文件

二进制文件:数据在内存中以二进制形式储存,如果不加转换得输出到外存,就是二进制文件

文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转换以ASCII码的形式存储的文件就是文本文件。

对应到数据就是

字符:一律ASCII码

数值型数据:既可以ASCII码,又可以二进制

例:

给一个整数10000(int),以ASCII码存就是占5个字节,但是以二进制形式就是4个字节


7、文件读取结束的判定

feof

注意:在文件读取中,绝不能用feof的返回值来判断文件是否结束,而是应用于文件已经结束了,是读取失败结束的,还是遇到文件结尾结束的。

文件正常结束会返回一个非0的值

ferror

不为-1就是打开失败


 所以到底该怎么判断文件结束?

对于文本文件,判断返回值是否未EOF(end of file)

对于二进制,fread在读取时,返回的是实际读取完整元素的个数,如果发现读取到的完整元素的个数<指定元素的个数,这就是最后一次读取了(但实际上在系统调用read的层面,有很多情况都能导致读到的完整元素个数(ssize_t)<指定元素个数,此处我们不做考虑)。


用上面的方法得知文件结束后我们再用feof函数

例:

假设test.txt文件中有一份代码,要求把test.txt文件拷贝一份,生成test2.txt

test.txt

int main()
{FILE* pfread = fopen("test.txt", "r");if (pfread == NULL){perror("fopen\n");}//写文件FILE* pfwrite = fopen("test2.txt", "w");if (pfwrite == NULL){//这次打开失败了说明第一次打开成功了,所以得释放第一个fclose(pfread);pfread = NULL;return 1;}//从pfread里读,写到pfwrite里int ch = 0;while ((ch = fgetc(pfread)) != EOF){fputc(ch, pfwrite);}//关文件fclose(pfread);pfread = NULL;fclose(pfwrite);pfwrite = NULL;
}

运行之后

test2果然也有了相同的代码

下来该判断文件如何结束的了

if (ferror(pfread))puts("I/O error when reading\n");else if (feof(pfread))puts("EOF reach successfully\n");

(正常结束了)


8、文件缓冲区

先给到大家一张图,然后再举一个例子

先举个现实生活中的例子

  比如你在北京,你的朋友在济南,你想给他寄一个东西,所以你就跑到你们学校的快递站,填好信息。

   此时你的快递就被寄出去了吗?没有,为什么?为什么不马上寄出去?

   如果说你刚走了,快递被一辆车寄出去了,又有一个人来了,填的信息也是寄向济南,然后再派一辆车寄吗?这成本太高了,肯定不能这么做

   所以怎么办? 快递站有一个专门放寄往济南的柜子,柜子满了再寄,也就是要等一波寄往济南的快递,然后只派一辆车寄,这成本就小很多了呀

对应于计算机我们再举一个例子

比如我写数据,我想往硬盘上写,但是这数据不是你能写的,你得告诉操作系统给你写、

操作系统是很忙的,你跟操作系统说:哥们,往磁盘写个数据,操作系统停下手上的活给你写;过一会儿你又说:哥们,往磁盘写个数据……那操作系统什么都不干,刚给你写数据?

那效率太低了。这个时候就要用的缓冲区了

你往磁盘写数据,先往缓冲区上写,缓冲区满了,操作系统再一波往磁盘上写。

所以缓冲区是很有必要的

注意:这波我们学的缓冲区是C语言给我们提供的,也就是说是一个语言层的缓冲区


 总结

 做总结,还是那句话,这篇博客只能带大家对文件有一个浅显的认识,在正式学了操作系统的基础IO和文件系统,才能对这些有更深刻的认识。

水平有限,还请各位大佬指正。如果觉得对你有帮助的话,还请三连关注一波。希望大家都能拿到心仪的offer哦。

每日gitee侠:今天你交gitee了嘛

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

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

相关文章

QML 项目中使用 Qt Design Studio 生成的UI界面

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 前言 今天来和大家聊一下 Qt Design Studio 这个软件。这个软件的主要功能是用来快速完成 UI 界面&#xff0c;就和 widget 中的 desig…

Vue.js 中使用 Watch 选项实现动态问题判断与展示答案

组件结构 以下是组件的基本结构&#xff1a; <template><div><!-- 输入框&#xff0c;用于输入问题 --><p>提出一个是/否问题&#xff1a;<input v-model"question" :disabled"loading" /></p><!-- 显示答案 --&…

栈实现后缀表达式的计算

后缀表达式计算 过程分析 中缀表达式 &#xff08;15&#xff09;*3 > 后缀表达式 153* (可参考这篇文章&#xff1a;中缀转后缀) 第一步&#xff1a;我们从左至右扫描 后缀表达式(已经存放在一个字符数组中)&#xff0c;遇到第一个数字字符 ‘1’ 放入栈中第二步&#xf…

市场复盘总结 20240103

仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整 昨日回顾: 方法一:指标选股 select * from dbo.ResultAll where 入选类型 like %指标选股% and 入选日期=20240103;方法二:趋势选股法 1、最低价持续3日上涨 2、均价…

新的一年,新的征程,35岁,再出发!!

2024&#xff0c;迈入新的征程&#xff01;35岁是人生的一个重要阶段&#xff0c;是积累经验、提升自我、迎接挑战的黄金时期&#xff0c;在接下来的日子&#xff1a; 设定目标&#xff1a;明确自己的长期和短期目标&#xff0c;确保自己始终朝着正确的方向前进。目标可以是职…

jmeter线程组

特点&#xff1a;模拟用户&#xff0c;支持多用户操作&#xff1b;可以串行也可以并行 分类&#xff1a; setup线程组&#xff1a;初始化 类似于 unittest中的setupclass 普通线程组&#xff1a;字面意思 teardown线程组&#xff1a;环境恢复&#xff0c;后置处理

机器视觉系统选型-选型-总结

一&#xff1a;明确需求 需求&#xff1a;镜面材质上的划痕检测&#xff0c;传送线上运动过程中拍照&#xff0c;无景深要求&#xff0c;传送线速度0.8m/s&#xff0c;产品间隔50mm 产品大小&#xff1a;100*80mm 工作距离限制&#xff1a;≤ 300mm 最小划痕宽度&#xff1a;0.…

shell编程二

shell 脚本规范 shell脚本文件需要以.sh结尾 第一个原因&#xff0c;让别人认的这个是shell脚本&#xff0c;sh后缀编辑时有高亮显示。 拓展名后缀,如果省略.sh则不易判断该文件是否为shell脚本 ​ # 执行脚本方式 1、 sh 脚本.sh 2、 bash 脚本.sh 3、 ./脚本.sh # 需要执行权…

NVMe SSD IO压力导致宕机案例解读-2

IOVA原理扩展介绍&#xff1a; 在Linux内核的I/O虚拟地址&#xff08;IOVA&#xff09;分配机制中存在两个影响高吞吐量I/O性能的问题 问题1&#xff1a;原有IOVA分配器在分配时可能需要对已分配的IOVA范围进行线性搜索&#xff0c;这种操作效率低下&#xff0c;尤其在大规模…

Day23

Day23 一,file类 1.1file类的初识 import java.io.File; import java.text.SimpleDateFormat;public class Test01 {/*** 知识点&#xff1a;File类* File&#xff0c;是文件和目录路径名的抽象表示* File只关注文件本身的信息&#xff0c;而不能操作文件里面的内容* * File…

AIGC时代-GPT-4和DALL·E 3的结合

在当今这个快速发展的数字时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为了我们生活中不可或缺的一部分。从简单的自动化任务到复杂的决策制定&#xff0c;AI的应用范围日益扩大。而在这个广阔的领域中&#xff0c;有两个特别引人注目的名字&#xff1a;GPT-4和D…

webpack 5 mode的作用和区别

通过选择 development, production 或 none 之中的一个&#xff0c;来设置 mode 参数&#xff0c;你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 developmen或者production. 为模块和 chunk 启用…

JUC原子操作类

原子操作类 基本类型原子类&#xff1a;AtomicInteger、AtomicBoolean、AtomicLong&#xff0c;常见API&#xff1a; get 获取当前值getAndSet 获取当前的值&#xff0c;并设置新的值getAndIncrement 获取当前的值&#xff0c;并自增getAndDecrement 获取当前的值&#xff0c;并…

洗地机怎么选?哪款洗地机好用?

选择洗地机前&#xff0c;我们需要对自己购买洗地机的需求做一个清洗的判断&#xff0c;吸尘器和扫地机智能解决地面基本的清洁问题&#xff0c;作为新兴的清洁工具洗地机越来越受大家的喜欢&#xff0c;洗地机的品类很多&#xff0c;洗地机到底该买哪款呢?我们先来看看挑选洗…

【现代控制理论】浙江大学 王建全教授

博主主页还有其他上万字精品笔记,欢迎自取 ​编辑P1[1.1.1]--视频&#xff1a;绪论.mp4_高清 1080P 11:19 这个视频是浙江大学的一门精品课程&#xff0c;主要介绍了现代控制理论的基本概念和发展历程。课程包括非线性系统理论、随机控制理论、自适应控制、模糊控制、鲁棒控制…

死锁-第三十四天

目录 什么是死锁 进程死锁、饥饿、死循环的区别 死锁产生的必要条件 什么时候会发生死锁 死锁的处理策略 本节思维导图 什么是死锁 每一个人都占有一个资源&#xff0c;同时又在等待另一个人手里的资源 进程死锁、饥饿、死循环的区别 死锁&#xff1a;各进程互相等待对…

drf序列化与序列化器的使用

序列化类的使用 使用序列化类实现五个接口功能&#xff0c;但是我们发现并没有做数据校验&#xff0c;也没有做反序列化&#xff0c;是我们自己手动去进反序列化&#xff0c;是我们自己使用for来进行拼接的&#xff0c;很不方便&#xff0c;我们可以使用一个drf自带的名叫序列…

ASP.Net实现海鲜添加(三层架构,异常处理)

演示功能&#xff1a; 点击启动生成页面 点击添加跳转新界面 此处设置文本框多行 点击Button添加 步骤&#xff1a; 1、建文件 下图是三层架构列表&#xff0c;Models里面有模拟数据库中列的类&#xff0c;DAL中有DBHelper和service,BLL中有BllManager文件用于ui界面直接调用…

SpringBoot之多环境开发配置

1 多环境开发配置 问题导入 在实际开发中&#xff0c;项目的开发环境、测试环境、生产环境的配置信息是否会一致&#xff1f;如何快速切换&#xff1f; 1.1 多环境启动配置 yaml文件多环境启动 不同环境使用—隔开 示例代码&#xff1a; spring:profiles:active: dev#生产…

易天推出10G SFP+ 可调 DWDM光模块:网络通信新升级

随着网络技术的飞速发展&#xff0c;为了满足用户对高速数据传输日益增长的需求。易天研发团队在原来的基础上推出了全新升级的10G SFP 可调 DWDM光模块&#xff0c;本文将详细介绍这款新升级光模块的特点、优势以及应用场景。 易天光通信10G SFP 可调 DWDM光模块具有出色的波…