速通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");//二进制写,wb

	if (pf == NULL)
	{
		perror("fopen\n");
	}
	fwrite(&s, sizeof(struct S), 1, pf);

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

我们看不懂,但是fread可以

struct S s = {0 };
	FILE* pf = fopen("test.txt", "rb");//二进制读,rb
	
	fread(&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);//a
			ch = fgetc(pf);
			printf("%c\n", ch);//b
			ch = 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了嘛

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FL Studio Producer Edition 21.2.2中文版所有插件版及使用教程

FL Studio 21.2.2中文版惯称水果编曲, 是一个完整的电音软件音乐制作环境或数字音频工作站。是现在流行的数字音频工作站之一,包括撰写,整理,记录,编辑,电音,混音和掌握专业品质的音乐。 FL Studio Producer Edition 21.2.2.3914 所有插件版是一款功能强大的软件音乐制作环境或…

第三十七周周报:文献阅读+掩码、多头注意力机制+位置编码

目录 摘要 Abstract 文献阅读&#xff1a;基于注意力的LSTM大地震预报网络 现有问题 提出方法 基于注意力的LSTM网络 研究实验 实验目的 数据集 评估指标 数据预处理和特征提取 结果讨论 Masked Self-Attention&#xff08;掩码自注意力&#xff09; Muti-Head S…

oracle 9i10g编程艺术-读书笔记2

配置Statspack 安装Statspack需要用internal身份登陆&#xff0c;或者拥有SYSDBA(connect / as sysdba)权限的用户登陆。需要在本地安装或者通过telnet登陆到服务器。 select instance_name,host_name,version,startup_time from v$instance;检查数据文件路径及磁盘空间&…

Primavera Unifier 项目控制延伸:Phase Gate理论:3/3

继续上一篇阶段Gate的具体内容 https://campin.blog.csdn.net/article/details/127827681https://campin.blog.csdn.net/article/details/127827681 阶段 3 研发 前述阶段的计划和安排都要在研发阶段执行起来&#xff0c;同时&#xff0c;最重要的产品设计和开发部分也需要在…

“TypeError: Cannot read properties of null (reading ‘getContext‘)“

目录 一、报错截图 二、使用场景 三、代码截图 四、报错原因 五、解决办法 一、报错截图 二、使用场景 第一次在vue项目种使用canvas&#xff0c;跟着网上教程做&#xff0c;标签canvas写好了&#xff0c;dom元素获取了&#xff0c;简单“画”了一下&#xff0c;运行之后报…

前端显示json格式化

实现效果 在前端页面上展示格式化的JSON数据可以通过以下步骤完成&#xff1a; 获取JSON数据&#xff1a;首先&#xff0c;你需要获取要展示的JSON数据。你可以从后端API获取数据&#xff0c;或者直接在前端定义一个JSON对象。 格式化JSON&#xff1a;使用JavaScript的JSON对…

电池管理系统BMS中SOC算法通俗解析(二)

下面简单介绍下我们BMS保护板使用的SOC估算方法。我们算法的主要是针对电流积分法计算SOC的局限性进行改进&#xff1a; ●电池包第一次上电使用开路电压法估算SOC。第一次上电&#xff0c;根据电池包厂家给出的电压和剩余容量二维关系图大概估算出目前电池包的剩余容量即SOC。…

音视频技术开发周刊 | 326

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 全球最强「开源版Gemini」诞生&#xff01;全能多模态模型Emu2登热榜&#xff0c;多项任务刷新SOTA 最强的全能多模态模型来了&#xff01;就在近日&#xff0c;智源研究院…

ES6之Proxy详解

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…

2024年第一天,先送一波福利!

▼最近直播超级多&#xff0c;预约保你有收获 近期直播&#xff1a;《LLM在电商搜索系统的应用案例实战》 —1— 2024 AIGC 技术体系领取福利 2023年是当之无愧的生成式 AI 元年&#xff0c;AIGC 的崛起深刻改变了我们的工作和生活&#xff0c;让我们看到了未来无限的可能性&am…

gzip引入后node_modules中.cache compression-webpack-plugin占用内存过多

1.Gzip Gzip&#xff08;GNU zip&#xff09;是一种常见的文件压缩格式和压缩算法&#xff0c;通常用于在 Web 服务器上对静态资源文件进行压缩&#xff0c;以减小文件大小并加快文件传输速度。在前端开发中&#xff0c;经常会使用 Gzip 压缩来优化网站的性能。 Gzip 压缩通过…

2024校招测试工程师笔试——经典错题记录和解析

大家好&#xff0c;这篇文章记录几个测开方向经典的例题&#xff0c;并给出相应解析&#xff0c;欢迎给出你的看法 下列关于软件性能测试的说法中&#xff0c;正确的是&#xff1a;&#xff08; &#xff09; A 性能测试的目的不是为了发现软件缺陷 B 压力测试与负载测试的目的…

windows11新装机,简单评测系统自带软件(基本涵盖日常所需应用)

新年将近&#xff0c;由于当年安排的失误&#xff0c;系统盘&#xff08;100G&#xff09;和照片视频盘&#xff08;4T&#xff09;容量不够了&#xff0c;大容量的那块机械盘放在机箱里就在耳朵根吵吵&#xff0c;烦得很&#xff0c;于是狠狠心决定扩容后重配重装。 2023年最后…

JMeter 简单使用

JMeter 简介 Apache JMeter 是一款流行的性能测试工具&#xff0c;可以用来模拟用户行为并对系统进行压力测试。 安装 官方网站&#xff1a;http://jmeter.apache.org/ 在window下解压后&#xff0c; 运行 “bin/jmeter.bat” Jmeter 支持中文&#xff0c; 启动 Jmeter 后&…

【LMM 007】Video-LLaVA:通过投影前对齐以学习联合视觉表征的视频多模态大模型

论文标题&#xff1a;Video-LLaVA: Learning United Visual Representation by Alignment Before Projection 论文作者&#xff1a;Bin Lin, Yang Ye, Bin Zhu, Jiaxi Cui, Munan Ning, Peng Jin, Li Yuan 作者单位&#xff1a;Peking University, Peng Cheng Laboratory, Sun …

云计算复习提纲

第一章 大数据的概念&#xff1a;海量数据的规模巨大到无法通过目前主流的计算机系统在合理时间内获取、存储、管理、处理并提炼以帮助使用者决策 大数据的特点&#xff1a;①数据量大&#xff0c;存储的数据量巨大&#xff0c;PB级别是常态&#xff1b;②多样&#xff0c;数…

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《考虑用户禀赋效应和环保意识不确定性的微电网鲁棒优化调度方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主的专栏栏目《论文与完整程序》 这个标题涉及到微电网系统的优化调度方法&#xff0c;特别考虑了两个重要方面&#xff1a;用户禀赋效应和环保意识的不确定性。以下是对标题中关键术语的解…

系统学习Python——装饰器:函数装饰器-[对方法进行装饰:基础知识]

分类目录&#xff1a;《系统学习Python》总目录 我们在前面的文章中编写了第一个基于类的tracer函数装饰器的时候&#xff0c;我们简单地假设它也应该适用于任何方法一一一被装饰的方法应该同样地工作&#xff0c;并且自带的self实例参数应该直接包含在*args的前面。但这一假设…

PHP特性知识点扫盲 - 下篇

概述 在实际的生产环境中遇到了实际需要解决的问题&#xff0c;需要把服务部署的方式梳理出来&#xff0c;在同一个服务器中部署多个PHP环境&#xff0c;架构图如下&#xff1a; 架构方案 在工作实践中遇到的很多问题的普遍性都是相通的&#xff0c;公司运行的可新项目都是版…

详细讲解MybatisPlus中的BaseMapper类中的CRUD功能(全)

目录 前言1. 基本概念2. CRUD2.1 插入2.2 删除2.3 修改2.4 查询 前言 大部分CRUD都来源这个类&#xff0c;对此有意义剖析&#xff0c;方便之后的功能开发 1. 基本概念 MyBatis-Plus&#xff08;简称MP&#xff09;是基于 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上…