【c语言】文件操作详解 - 从打开到关闭

news2024/11/27 9:12:01

在这里插入图片描述

文章目录

    • 1. 为什么使用文件?
    • 2. 什么是文件?
    • 3. 如何标识文件?
    • 4. 二进制文件和文本文件?
    • 5. 文件的打开和关闭
      • 5.1 流和标准流
        • 5.1.1 流
        • 5.1.2 标准流
      • 5.2 文件指针
      • 5.3 文件的打开和关闭
    • 6. 文件的读写顺序
      • 6.1 顺序读写函数
      • 6.2 对比一组函数
    • 7. 文件的随机读写
      • 7.1 fseek
      • 7.2 ftell
      • 7.3 rewind
    • 8. 文件读取结束的判定
      • 8.1 被错误使用的`feof`
    • 9. 文件缓冲区

1. 为什么使用文件?

如果没有文件,我们写的程序的数据存储在电脑的内存当中,如果程序退出,内存回收,数据就丢失了,再次运行程序时,看不到上次程序的数据,如果要将数据进行持久化的保存,我们可以使用文件。

2. 什么是文件?

硬盘或磁盘上的文件就叫做文件。
在程序设计中我们一般会谈两种文件:程序文件数据文件(从文件功能的角度来分类)

  1. 程序文件:
    包括程序的源程序文件,目标文件(windows环境后缀为.obj),可执行程序文件(windows环境后缀为.exe)
  2. 数据文件:
    文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。

3. 如何标识文件?

⼀个文件要有⼀个唯一的文件标识,以便用户识别和引用。其实就是文件名。
文件名包含3部分:文件路径 + 文件名主干 + 文件后缀
例如:

c:\code\test.txt

为了方便起见,文件标识常被称为文件名。

4. 二进制文件和文本文件?

根据数据的组织形式,数据文件被称为文本文件二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存的⽂件中,就是二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件

那么一个数据在文件中是如何存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以用ASCII码形式存储,也可以使用二进制形式存储。
以10000为例,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节。
在这里插入图片描述

5. 文件的打开和关闭

5.1 流和标准流

5.1.1 流

流(Stream)是一个抽象的概念,用于表示数据的流动。流可以是输入流(Input Stream)或输出流(Output Stream),分别用于从某个源读取数据和向某个目标写入数据。

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。
一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

5.1.2 标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动的时候,默认打开了3个流:

• stdin: 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输⼊流中读取数据。
• stdout: 标准输出流,大多数的环境中输出至显示器界面,\,printf函数就是将信息输出到标准输出流中。
• stderr: 标准错误流,⼤多数环境中输出到显示器界面。

这是默认打开了这三个流,我们使用scanfprintf等函数就可以直接进行输入输出操作的。
stdin、stdout、stderr三个流的类型是:FILE*,通常称为文件指针。
C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。

5.2 文件指针

缓冲文件系统中,关键的概念是文件类型指针,简称文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE.

例如vs2013stdio.h头文件中包含的文件有如下的定义

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指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。
比如:
在这里插入图片描述

5.3 文件的打开和关闭

文件在读写之前首先应当打开文件,使用结束之后应当关闭文件
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针指向该文件,相当于建立了文件和指针的关系。
ANSIC规定使用fopen函数来打开文件, fclose函数来关闭文件。

//打开文件
FILE* fopen(const char * filename, const char * mode);//第一个参数为文件名,第二个参数为文件的打开方式
//关闭文件
int fclose(FILE * stream);

mode表示文件的打开模式,下面都是文件的打开模式:

文件使用方式含义如果指定文件不存在
“r“(只读)为了输入数据打开一个已经存在的文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制⽂件出错
“wb”(只写)为了输出数据,打开一个二进制⽂件建立一个新的文件
“ab”(追加)向一个二进制⽂件尾添加数据建立一个新的文件
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建立一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个⼆进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个⼆进制文件,在文件尾进行读和写建立一个新的文件

代码实现:

int main()
{
	//打开文件
	//打开文件成功,返回有效的指针
	//打开失败,返回NULL
	FILE* pf = fopen("data.txt","w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件


	//关闭文件
	fclose(pf);

	pf = NULL; //关闭之后应将pf置为空,否则将会成为野指针

	return 0;
}

6. 文件的读写顺序

6.1 顺序读写函数

函数名功能适用于
fgetc字符输⼊函数所有输⼊流
fputc字符输出函数所有输出流
fgets文本行输⼊函数所有输⼊流
fputs文本行输出函数所有输出流
fscanf格式化输⼊函数所有输⼊流
fprintf格式化输出函数所有输出流
fread⼆进制输⼊文件
fwrite⼆进制输出文件

举例fputc

int main()
{
	FILE* pf = fopen("data.txt","w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	fputc('d', pf);
	fputc('e', pf);

	//关闭文件
	fclose(pf);

	pf = NULL;

	return 0;
}

我们可以看到data.txt文件中多了abcde
在这里插入图片描述
举例fgutc

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = fgetc(pf);
	printf("%c", ch);

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

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

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

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

	//关闭文件
	fclose(pf);

	pf = NULL;

	return 0;
}

程序的运行结果:
在这里插入图片描述

上面说的适用于所有输入流⼀般指适用于标准输入流和其他输入流(如文件输入流);所有输出流⼀般指适用于标准输出流和其他输出流(如文件输出流)。

int main()
{
	int ch = fgetc(stdin);//从键盘上(标准输入流)上读取
	fputc(ch,stdout); //将字符输出(写)到屏幕(标准输出流)

	return 0;
}

6.2 对比一组函数

scanf/printf:针对标准输入流/标准输出流的 格式化 输入/输出函数
fscanf/fprintf:针对所有输入流/所有输出流的 格式化 输入/输出函数
sscanf/sprintf:将格式化的数据转化成字符串/从字符串中提取格式化数据
sprinft: 从字符串中提取格式化的数据(将字符串转化为格式化数据)
sscanf: 将格式化的数据写到字符串中(将格式化的数据转化成字符串)

7. 文件的随机读写

7.1 fseek

根据文件指针的位置和偏移量来定位文件指针(文件内容的光标)。

int fseek ( FILE * stream, long int offset, int origin );//参数分别是文件指针,偏移量(可以是正值,也可以是负值),起始位置

代码演示:

int main()
{
	//1.打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//2.读文件

	//fseek
	int ch = 0;
	fseek(pf, 4, SEEK_SET);//SEEK_SET起始位置
	ch = fgetc(pf);
	printf("%c\n", ch);
	fseek(pf, 2, SEEK_CUR);//SEEK_CUR当前位置
	ch = fgetc(pf);
	printf("%c\n", ch);
	fseek(pf, -2, SEEK_END);//SEEK_END文件末尾
	ch = fgetc(pf);
	printf("%c\n", ch);

	//3.关闭文件
	fclose(pf);

	pf = NULL;

	return 0;
}

在这里插入图片描述
输出结果:
在这里插入图片描述

7.2 ftell

返回文件指针相对于起始位置的偏移量

long int ftell ( FILE * stream );

示例:

int main()
{
	//1.打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//2.读文件
	int ch = 0;
	ch = fgetc(pf);
	printf("%c\n", ch);//a
	ch = fgetc(pf);
	printf("%c\n", ch);//b

	//fseek
	fseek(pf, -2, SEEK_END);//SEEK_END文件末尾
	ch = fgetc(pf);
	printf("%c\n", ch);//e

	//输出文件指针相较于起始位置的偏移量
	printf("%d\n",ftell(pf));

	//3.关闭文件
	fclose(pf);

	pf = NULL;

	return 0;
}

输出结果:
在这里插入图片描述

7.3 rewind

让文件指针的位置回到文件的起始位置。

void rewind ( FILE * stream );

代码演示:

int main()
{
	//1.打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//2.读文件
	int ch = 0;
	ch = fgetc(pf);
	printf("%c\n", ch);//a
	ch = fgetc(pf);
	printf("%c\n", ch);//b

	//fseek
	fseek(pf, -2, SEEK_END);//SEEK_END文件末尾
	ch = fgetc(pf);
	printf("%c\n", ch);//e

	//输出文件指针相较于起始位置的偏移量
	printf("%d\n",ftell(pf)); //5

	rewind(pf);
	ch = fgetc(pf);
	printf("%c\n", ch);

	//3.关闭文件
	fclose(pf);

	pf = NULL;

	return 0;
}

输出结果:
在这里插入图片描述
可以看到光标又指回了a

8. 文件读取结束的判定

8.1 被错误使用的feof

  • EOF - end of file :文件结束的标志
    所以大家都会认为feof函数是用来判断文件是否结束的,但是其实并不是

feof的作用: 当文件读取结束的时候,判断读取结束的原因是不是:遇到文件结尾结束

在读取文件的过程中,有可能读取文件结束,结束的原因是:

  1. 遇到文件结尾
  2. 遇到错误了

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

  • fgetc判断是否为 EOF 。
  • fgets 判断返回值是否为 NULL .

2.二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:

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

9. 文件缓冲区

ANSIC 标准采用缓冲文件系统处理数据文件的,所谓的缓冲文件系统是指系统自动地在内存中为程序中的每一个正在使用的文件开辟一块文件缓冲区。从内存向磁盘输出数据先会送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据c编译系统决定的。
在这里插入图片描述
代码验证:

#include <stdio.h>
#include <windows.h>
int main()
{
	FILE* pf = fopen("data.txt","w");
	fputs("abcdef",pf);   //先将abcdef放在输出缓冲区
	printf("睡眠10秒,打开data.txt发现没有内容\n");
	Sleep(10000);

	printf("刷新缓冲区\n");
	fflush(pf);  //刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
	printf("再睡眠10秒,再次打开data.txt文件,文件有内容了\n");
	Sleep(10000);


	fclose(pf);
	//注:fclose在关闭文件的时候,也会刷新缓冲区 
	pf = NULL;

	return 0;
}

结论:
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。


如果这篇文章对你有帮助,记得点赞,评论+收藏 ,最后别忘了关注作者,作者将带领你探索更多关于c语言方面的问题。

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

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

相关文章

004 逻辑变量与运算

当0和1表示逻辑状态时&#xff0c;两个二进制数码按照某种特定的因果关系进行的运算——就叫&#xff1a;逻辑运算 1.二值逻辑变量与基本逻辑运算 逻辑代数: 与普通代数不同,逻辑代数中的变量只有0和1两个可取值&#xff0c;它们分别用来表示完全两个对立的逻辑状态 逻辑运…

Deepnote、JupyterLab、Google Colab、Amazon SageMaker、VS Code对比

功能比较 平台语言支持扩展性数据连接可视化能力DeepnotePython、R、SQL中等&#xff0c;依赖云端支持主要云平台&#xff08;BigQuery、Snowflake等&#xff09;内置仪表盘与交互图表JupyterLab多种语言&#xff0c;插件支持广泛极高&#xff0c;完全可自定义使用库&#xff…

网络安全中的数据科学如何重新定义安全实践?

组织每天处理大量数据&#xff0c;这些数据由各个团队和部门管理。这使得全面了解潜在威胁变得非常困难&#xff0c;常常导致疏忽。以前&#xff0c;公司依靠 FUD 方法&#xff08;恐惧、不确定性和怀疑&#xff09;来识别潜在攻击。然而&#xff0c;将数据科学集成到网络安全中…

C语言数据结构与算法--简单实现队列的入队和出队

&#xff08;一&#xff09;队列的基本概念 和栈相反&#xff0c;队列(Queue)是一种先进先出&#xff08;First In First Out&#xff09;的线性表。只 允许在表的一端进行插入&#xff0c;而在另一端删除元素&#xff0c;如日常生活中的排队现象。队列中 允许插入的一端叫队尾…

快速理解微服务中Sentinel怎么实现限流

Sentinel是通过动态管理限流规则&#xff0c;根据定义的规则对请求进行限流控制。 一.实现步骤 1.定义资源&#xff1a;在Sentinel中&#xff0c;资源可以是URL、方法等&#xff0c;用于标识需要进行限流的请求&#xff1b;(在Sentinel中&#xff0c;需要我们去告诉Sentinel哪些…

matlab根据excel表头筛选表格数据

有如下表格需要筛选&#xff1a; 如果要筛选style中的A&#xff0c;color中的F2&#xff0c;num中的3。 代码如下&#xff1a; clear;clc; file_Pathstrcat(F:\csdn\,test1.xlsx); %表格路径、文件名 E1readtable(file_Path,Sheet,1); %读取表格中的字母和数字,1代表第一个…

学习日志016--python实现双向循环列表与链栈

python中一些复合数据结构通过类的封装来实现的。双向循环链表与链栈也在其中。 双向循环链表 双向循环链表是一种特殊类型的链表&#xff0c;它结合了双向链表和循环链表的特点。在双向循环链表中&#xff0c;每个节点不仅包含数据&#xff0c;还持有指向前一个和后一个节点的…

【Docker】常用命令汇总

Docker 是1个开源的应用容器引擎&#xff0c;基于Go 语言并遵从 Apache2.0 协议开源。 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器是完全使用沙箱机制&#xff0c;相…

QT QRadioButton控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…

Docker部署mysql:8.0.31+dbsyncer

Docker部署mysql8.0.31 创建本地mysql配置文件 mkdir -p /opt/mysql/log mkdir -p /opt/mysql/data mkdir -p /opt/mysql/conf cd /opt/mysql/conf touch my.config [mysql] #设置mysql客户端默认字符集 default-character-setUTF8MB4 [mysqld] #设置3306端口 port33…

[SUCTF 2019]EasySQL--详细解析

信息搜集 进入界面是一个搜索框&#xff1a; 查看一下源代码&#xff0c;显示是POST传参&#xff1a; 随便上传个数字1&#xff1a; 抓包测试一下闭合&#xff0c;发现以双引号闭合会回显nonono,单引号闭合则无回显。 由于没有报错信息&#xff0c;所以我们不能确定具体的闭…

警钟长鸣,防微杜渐,遨游防爆手机如何护航安全生产?

近年来&#xff0c;携非防爆手机进入危险作业区引发爆炸的新闻屡见报端。2019年山西某化工公司火灾&#xff0c;2018年延安某煤业瓦斯爆炸&#xff0c;均因工人未用防爆手机产生静电打火引发。涉爆行业领域企业量大面广&#xff0c;相当一部分企业作业场所人员密集&#xff0c;…

【智能流体力学】RAG大模型方法:解决固体力学和流体动力学问题

【使用 AutoGen + GPT-4o + Chainlit UI 进行工程仿真的对话式多智能体 AI 聊天机器人】 本项目构建了一个由多个AI代理组成的系统,这些代理通过使用Microsoft AutoGen进行对话交互,能够自主地创建和仿真固体力学(FEA)和流体动力学(CFD)问题。每个AI代理都擅长规划、问题…

Redis与MySQL如何保证数据一致性

Redis与MySQL如何保证数据一致性 简单来说 该场景主要发生在读写并发进行时&#xff0c;才会发生数据不一致。 主要流程就是要么先操作缓存&#xff0c;要么先操作Redis&#xff0c;操作也分修改和删除。 一般修改要执行一系列业务代码&#xff0c;所以一般直接删除成本较低…

Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导 一、前言 在充满活力与激情的校园生活中&#xff0c;校运会不仅是…

【西瓜书】神经网络-MP神经元、感知机和多层网络

神经网络&#xff08;neural networks&#xff09;的定义&#xff1a;神经网络是由具有适应性的简单单元组成的广泛并行互联的网络&#xff0c;它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。&#xff08;T. Kohonen 1988年在Neural Networks创刊号上给出的定义…

《安富莱嵌入式周报》第346期:开源2GHz带宽,12bit分辨率,3.2Gsps采样率示波,开源固件安全分析器, 开源口袋电源,开源健康测量,FreeCAD

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频&#xff1a; https://www.bilibili.com/video/BV1TYBhYKECK/ 《安富莱嵌入式周报》第346期&#xff1a;开源2GHz带…

介绍一下atoi(arr);(c基础)

hi , I am 36 适合对象c语言初学者 atoi(arr)&#xff1b;是返回整数(int型)&#xff0c;整数是arr数组中字符中数字 格式 #include<stdio.h> atoi(arr); 返回值arr数组中的数字 未改变arr数组 #include<stdlib.h>//atoi(arr); 返 <stdlib> int main(…

Docker: 教程07 - ( 如何对 Docker 进行降级和升级)

如果我们使用 docker 来管理容器&#xff0c;那么保持 docker 引擎的更新将会是十分重要的&#xff0c;这一篇文章我们将会讨论如何对Docker 进行降级和升级。 准备工作 - docker 环境 我们需要拥有一个安装好 docker 的运行环境。 如果你需要了解如何安装 docker 可以通过如…

ArcGIS pro中的回归分析浅析(加更)关于广义线性回归工具的补充内容

在回归分析浅析中篇的文章中&#xff0c; 有人问了一个问题&#xff1a; 案例里的calls数据貌似离散&#xff0c;更符合泊松模型&#xff0c;为啥不采用泊松而采用高斯呢&#xff1f; 确实&#xff0c;在中篇中写道&#xff1a; 在这个例子中我们为了更好地解释变量&#x…