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

在这里插入图片描述

文章目录

    • 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语言方面的问题。

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

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

相关文章

2024-11-23 队列及顺序存储实现

2.3.1 队列及顺序存储实现 与堆栈类似&#xff0c;队列也是一种受限制的线性表。 其实我们在日常生活中经常会碰到排队。我们来观察一下&#xff0c;什么叫做队列&#xff0c;里面有两个最基本的操作&#xff0c;一个叫做入队&#xff0c;一个叫做出队。也就是你能加入这个队…

卷积神经网络学习记录

目录 神经网络基础定义&#xff1a; 基本组成部分 工作流程 卷积层&#xff08;卷积定义&#xff09;【CONV】&#xff1a; 卷积层&#xff08;Convolutional Layer&#xff09; 特征提取&#xff1a;卷积层的主要作用是通过卷积核&#xff08;或滤波器&#xff09;运算提…

数据结构初阶---复杂度

一、数据结构前言 1.数据结构与算法 数据结构(Data Structure)&#xff1a;是计算机组织、存储数据的一种方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合。 算法(Algorithm)&#xff1a;就是定义良好的计算过程&#xff0c;他取一个或一组的值为输入&am…

二叉树的层次遍历

二叉树的层次遍历 题目 https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ 描述 给你一个二叉树,请你返回其按 层次遍历 得到的节点值(即逐层地,从做到右访问所有节点) 代码实现 通过两个数组来交替打印 class Solution(object):def levelOrder

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

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

【Linux系统】—— 基本指令(四)

【Linux系统】—— 基本指令&#xff08;三&#xff09; 1「find」指令2 「grep」指令2.1 初识「grep」指令2.2 「grep」指令 选项 3 打包压缩基本知识4 「zip / unzip」指令5「tar」命令6 文件互传6.1 Linux 与 Windows 互传6.1.1 Linux向Windows传输6.1.2 Windows向Linux传输…

将django+vue项目发布部署到服务器

1.部署django后端服务 部署架构 1.1 下载依赖插件 pip3.8 freeze > requirements.txt1.2 安装依赖插件 pip3 install -r requirements.txt1.3 安装mysql数据库 apt install mysql-server初始化数据库 CREATE USER admin% IDENTIFIED WITH mysql_native_password BY 123…

网络层协议IP

对于网络层我们直接通过IP协议来了解其内容 一.IP协议 首先我们先来了解几个概念&#xff1a; 主机&#xff1a;配有IP地址&#xff0c;但是不进行路由控制的设备 路由器&#xff1a;配有IP地址&#xff0c;同时进行路由控制的设备 节点&#xff1a;主机和路由器的统称 所以现在…

AIGC-----AIGC在虚拟现实中的应用前景

AIGC在虚拟现实中的应用前景 引言 随着人工智能生成内容&#xff08;AIGC&#xff09;的快速发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术的应用也迎来了新的契机。AIGC与VR的结合为创造沉浸式体验带来了全新的可能性&#xff0c;这种组合不仅极大地降低了VR内容的…

如何利用 Puppeteer 的 Evaluate 函数操作网页数据

介绍 在现代的爬虫技术中&#xff0c;Puppeteer 因其强大的功能和灵活性而备受青睐。Puppeteer 是一个用于控制 Chromium 或 Chrome 浏览器的 Node.js 库&#xff0c;提供了丰富的 API 接口&#xff0c;能够帮助开发者高效地处理动态网页数据。本文将重点讲解 Puppeteer 的 ev…

【运维】 使用 shell 脚本实现类似 jumpserver 效果实现远程登录linux 服务器

实现效果 通过序号选择登录&#xff1a; 配置证书登录 配置证书登录可以免去每次都输入密码的麻烦。详见另一篇博文&#xff1a; 【ssh】使用秘钥对&#xff08;公钥/私钥&#xff09;登录linux主机以及原理介绍 自动登录脚本 直接复用以下脚本即可&#xff0c;在 server…

sqlmap学习,打靶sqli-labs.(1-19)

前言&#xff1a;用于学习sqlmap的简单使用&#xff0c;使用sqli-labs靶场进行测试。 当然,在实战中,考虑的更多&#xff0c;例如如何隐藏自己(特征码),编码加解密、sqlmap抓包调试分析等... 不过那些都是后话&#xff0c;太遥远...基础NO.1&#xff01;&#xff01; 先贴上我…

A045-基于spring boot的个人博客系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

[RabbitMQ] 保证消息可靠性的三大机制------消息确认,持久化,发送方确认

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

Unity中动态生成贴图并保存成png图片实现

实现原理&#xff1a; 要生成长x宽y的贴图&#xff0c;就是生成x*y个像素填充到贴图中&#xff0c;如下图&#xff1a; 如果要改变局部颜色&#xff0c;就是从x1到x2(x1<x2),y1到y2(y1<y2)这个范围做处理&#xff0c; 或者要想做圆形就是计算距某个点&#xff08;x1,y1&…

sklearn学习

介绍&#xff1a;scaler&#xff1a;换算的意思 1. 归一化MinMaxScaler() 归一化的意思是将一堆数&#xff0c;如果比较离散&#xff0c;为了让数据更适合模型训练&#xff0c;将离散的数据压缩到0到1之间&#xff0c;以方便模型更高效优质的学习&#xff0c;而对数据的预处理…

windows下安装wsl的ubuntu,同时配置深度学习环境

写在前面&#xff0c;本次文章只是个人学习记录&#xff0c;不具备教程的作用。个别信息是网上的&#xff0c;我会标注&#xff0c;个人是gpt生成的 安装wsl 直接看这个就行&#xff1b;可以不用备份软件源。 https://blog.csdn.net/weixin_44301630/article/details/1223900…

Flutter:启动屏逻辑处理02:启动页

启动屏启动之后&#xff0c;制作一个启动页面 新建splash&#xff1a;view 视图中只有一张图片sliding.png就是我们的启动图 import package:flutter/material.dart; import package:get/get.dart; import index.dart; class SplashPage extends GetView<SplashController…

【AIGC】如何准确引导ChatGPT,实现精细化GPTs指令生成

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AIGC | 提示词Prompt应用实例 文章目录 &#x1f4af;前言&#x1f4af;准确引导ChatGPT创建爆款小红书文案GPTs指令案例&#x1f4af; 高效开发GPTs应用的核心原则明确应用场景和目标受众构建多样化风格模板提问与引…

【通俗理解】隐变量的变分分布探索——从公式到应用

【通俗理解】隐变量的变分分布探索——从公式到应用 关键词提炼 #隐变量 #变分分布 #概率模型 #公式推导 #期望最大化 #机器学习 #变分贝叶斯 #隐马尔可夫模型 第一节&#xff1a;隐变量的变分分布的类比与核心概念【尽可能通俗】 隐变量的变分分布就像是一场“捉迷藏”游戏…