【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. 什么是文件?
硬盘或磁盘上的文件就叫做文件。
在程序设计中我们一般会谈两种文件:程序文件、数据文件(从文件功能的角度来分类)
- 程序文件:
包括程序的源程序文件,目标文件(windows环境后缀为.obj),可执行程序文件(windows环境后缀为.exe) - 数据文件:
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。
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: 标准错误流,⼤多数环境中输出到显示器界面。
这是默认打开了这三个流,我们使用scanf
、printf
等函数就可以直接进行输入输出操作的。
stdin、stdout、stderr
三个流的类型是:FILE*
,通常称为文件指针。
C语言中,就是通过 FILE*
的文件指针来维护流的各种操作的。
5.2 文件指针
缓冲文件系统中,关键的概念是文件类型指针,简称文件指针。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE
.
例如vs2013
下stdio.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()
{//打开文件//打开文件成功,返回有效的指针//打开失败,返回NULLFILE* 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.读文件//fseekint 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);//ach = fgetc(pf);printf("%c\n", ch);//b//fseekfseek(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);//ach = fgetc(pf);printf("%c\n", ch);//b//fseekfseek(pf, -2, SEEK_END);//SEEK_END文件末尾ch = fgetc(pf);printf("%c\n", ch);//e//输出文件指针相较于起始位置的偏移量printf("%d\n",ftell(pf)); //5rewind(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.文本文件读取是否结束,判断返回值是否为 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语言方面的问题。
相关文章:

【c语言】文件操作详解 - 从打开到关闭
文章目录 1. 为什么使用文件?2. 什么是文件?3. 如何标识文件?4. 二进制文件和文本文件?5. 文件的打开和关闭5.1 流和标准流5.1.1 流5.1.2 标准流 5.2 文件指针5.3 文件的打开和关闭 6. 文件的读写顺序6.1 顺序读写函数6.2 对比一组…...

Flink Sink的使用
经过一系列Transformation转换操作后,最后一定要调用Sink操作,才会形成一个完整的DataFlow拓扑。只有调用了Sink操作,才会产生最终的计算结果,这些数据可以写入到的文件、输出到指定的网络端口、消息中间件、外部的文件系统或者是…...
pcl::PointCloud<PointType>::Ptr extractedCloud; 尖括号里的值表示什么含义?
在C中,pcl::PointCloud<PointType>::Ptr是一种智能指针,它是Point Cloud Library (PCL)中用于管理pcl::PointCloud对象的智能指针类型。这里的<pcl::PointCloud<PointType>::Ptr>尖括号里的值表示智能指针所指向的对象类型。 让我们分…...

《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试
一、论文概述 基于FPGA的便携式PWM方波信号发生器是一篇由任青颖、庹忠曜、黄洵桢、李智禺和张贤宇 等人发表的一篇期刊论文。该论文主要研究了一种新型的信号发生器,旨在解决传统PWM信号发生器在移动设备信号调控中存在的精准度低和便携性差的问题 。其基于现场可编…...

VsCode 插件推荐(个人常用)
VsCode 插件推荐(个人常用)...

路由策略与路由控制实验
AR1、AR2、AR3在互联接口、Loopback0接口上激活OSPF。AR3、AR4属于IS-IS Area 49.0001,这两者都是Level-1路由器,AR3、AR4的系统ID采用0000.0000.000x格式,其中x为设备编号 AR1上存在三个业务网段A、B、C(分别用Loopback1、2、3接…...
训练的decoder模型文本长度不一致,一般设置为多大合适,需要覆盖最长的文本长度么
在训练解码器模型时,文本长度不一致是常见的情况,需要根据任务的特性和数据集的长度分布来设置合理的最大长度 (max_length)。以下是一些指导原则,帮助你设置合适的最大长度: 1. 是否需要覆盖最长文本长度 覆盖最长文本长度: 如果任务对完整性要求很高(例如生成数学公式、…...

过滤条件包含 OR 谓词,如何进行查询优化——OceanBase SQL 优化实践
这篇博客涉及两个点,一个是 “OR Expansion 改写”,另一个是 “基于代价的改写”。 背景 在写SQL查询时,难以避免在过滤条件中使用 OR 谓词,但其往往会导致索引利用效率下降的问题 。本文将分享如何通过查询改写的2种方式进行优化…...

通过异步使用消息队列优化秒杀
通过异步使用消息队列优化秒杀 同步秒杀流程异步优化秒杀异步秒杀流程基于lua脚本保证Redis操作原子性代码实现阻塞队列的缺点 同步秒杀流程 public Result seckillVoucher(Long voucherId) throws InterruptedException {SeckillVoucher seckillVoucher iSeckillVoucherServi…...

AI产业告别“独奏”时代,“天翼云息壤杯”高校AI大赛奏响产学研“交响乐”
文 | 智能相对论 作者 | 陈泊丞 人工智能产业正在从“独奏”时代进入“大合奏”时代。 在早期的AI发展阶段,AI应用主要集中在少数几个领域,如语音识别、图像处理等。这些领域的研究和开发工作往往由少数几家公司或研究机构即可独立完成,犹…...

Hot100 - 字母异位词分组
Hot100 - 字母异位词分组 最佳思路:排序 时间复杂度: O(nmlogm),其中 n 为 strs 数组的长度,m 为每个字符串的长度。 代码: class Solution {public List<List<String>> groupAnagrams(String[] strs) …...

力扣hot100-->排序
排序 1. 56. 合并区间 中等 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输…...

【VRChat 全身动捕】VIVE 手柄改 tracker 定位器教程,低成本光学动捕解决方案(持续更新中2024.11.26)
更新 0.0.1(2024/11/26): 1.解决了内建蓝牙无法识别、“steamVR 蓝牙不可用” 的解决方案 2.解决了 tracker 虽然建立了连接但是在 steamVR 界面上看不到的问题 3.解决了 VIVE 基站1.0 无法被蓝牙识别 && 无法被 steamVR 搜索到 &…...

【Nginx】核心概念与安装配置解释
文章目录 1. 概述2. 核心概念2.1.Http服务器2.2.反向代理2.3. 负载均衡 3. 安装与配置3.1.安装3.2.配置文件解释3.2.1.全局配置块3.2.2.HTTP 配置块3.2.3.Server 块3.2.4.Location 块3.2.5.upstream3.2.6. mine.type文件 3.3.多虚拟主机配置 4. 总结 1. 概述 Nginx是我们常用的…...

Qt界面篇:QMessageBox高级用法
1、演示效果 2、用法注意 2.1 设置图标 用于显示实际图标的pixmap取决于当前的GUI样式。也可以通过设置icon pixmap属性为图标设置自定义pixmap。 QMessageBox::Icon icon(...
【二叉树】【2.1遍历二叉树】【刷题笔记】【灵神题单】
关注二叉树的三个问题: 什么情况适合自顶向下?什么时候适合用自底向上?一般来说,DFS的递归边界是空节点,什么情况下要额外把叶子节点作为递归边界?在什么情况下,DFS需要有返回值?什…...
Mongo数据库 --- Mongo Pipeline
Mongo数据库 --- Mongo Pipeline 什么是Mongo PipelineMongo Pipeline常用的几个StageExplanation with example:MongoDB $matchMongoDB $projectMongoDB $groupMongoDB $unwindMongoDB $countMongoDB $addFields Some Query Examples在C#中使用Aggreagtion Pipeline**方法一: …...

Adobe Illustrator 2024 安装教程与下载分享
介绍一下 下载直接看文章末尾 Adobe Illustrator 是一款由Adobe Systems开发的矢量图形编辑软件。它广泛应用于创建和编辑矢量图形、插图、徽标、图标、排版和广告等领域。以下是Adobe Illustrator的一些主要特点和功能: 矢量绘图:Illustrator使用矢量…...
javax.xml.ws.soap.SOAPFaultException: ZONE_OFFSET
javax.xml.ws.soap.SOAPFaultException 表示 SOAP 调用过程中发生了错误,并且服务端返回了一个 SOAP Fault。 错误信息中提到的 ZONE_OFFSET 可能指的是时区偏移量。在日期和时间处理中,时区偏移量是指格林威治标准时间 (GMT) 的偏移量。如果服务期望特…...
常用的数据结构
队列(FIFO) 栈(LIFO) 链表 hash表 hash冲突处理 开放式寻址 线性探测 表示依次检查索引为 hash(key) + 1、hash(key) + 2 ... 的位置。i 是冲突后的探查步数。公式:hash(i) = (hash(key) + i) % TableSize二次探查 规则:冲突后探查的步长是平方递增的,例如,检查位置为 hash…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...

链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...

算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...