【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()
{//打开文件//打开文件成功,返回有效的指针//打开失败,返回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. 遇到文件结尾
  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.rhkb.cn/news/478864.html

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

相关文章

视频质量评价SimpleVQA

目录 一、研究意义例子 二、介绍三、文章解读3.1 论文动机3.2论文思路3.3方法3.3.1网络框架3.3.2公式解读3.3.3核心创新3.3.4理解 &#xff01;&#xff01;&#xff01;作者对模型的改进本人算法框体视频抽帧美学特征提取网络&#xff1a;3.3.5实验细节&#xff1a; 四、代码复…

【真实场景面试问题-2】

1 介绍一下低功耗设计手段–clock gating 和 clock domain&#xff1b; 芯片的低功耗设计尤其是在关注能耗的场景&#xff1a;移动设备、物联网和嵌入式系统&#xff1b;时钟门控&#xff08;Clock Gating&#xff09;和时钟域&#xff08;Clock Domain&#xff09;是两种常用…

c++编程玩转物联网:使用芯片控制8个LED实现流水灯技术分享

在嵌入式系统中&#xff0c;有限的GPIO引脚往往限制了硬件扩展能力。74HC595N芯片是一种常用的移位寄存器&#xff0c;通过串行输入和并行输出扩展GPIO数量。本项目利用树莓派Pico开发板与74HC595N芯片&#xff0c;驱动8个LED实现流水灯效果。本文详细解析项目硬件连接、代码实…

《白帽子讲Web安全》13-14章

《白帽子讲Web安全》13-14章 《白帽子讲Web安全》13-14章13、应用层拒绝服务攻击13.1、DDOS简介13.2、应用层DDOS13.2.1、CC攻击13.2.2、限制请求频率13.2.3、道高一尺&#xff0c;魔高一丈 13.3、验证码的那些事儿13.4、防御应用层DDOS13.5、资源耗尽攻击13.5.1、Slowloris攻击…

Linux—进程概念学习-03

目录 Linux—进程学习—31.进程优先级1.1Linux中的进程优先级1.2修改进程优先级—top 2.进程的其他概念3.进程切换4.环境变量4.0环境变量的理解4.1环境变量的基本概念4.2添加环境变量—export4.3Linux中环境变量的由来4.4常见环境变量4.5和环境变量相关的命令4.6通过系统调用获…

pikachu文件上传漏洞通关详解

声明&#xff1a;文章只是起演示作用&#xff0c;所有涉及的网站和内容&#xff0c;仅供大家学习交流&#xff0c;如有任何违法行为&#xff0c;均和本人无关&#xff0c;切勿触碰法律底线 目录 概念&#xff1a;什么是文件上传漏洞一、客户端check二、MIME type三、getimagesi…

从零开始学GeoServer源码(二)添加支持arcgis切片功能

文章目录 参考文章环境背景1、配置打包好的程序1.1、下载GeoServer的war包1.2、下载GeoWebCache1.3、拷贝jar包1.4、修改配置文件1.4.1、拷贝geowebcache-arcgiscache-context.xml1.4.2、修改geowebcache-core-context.xml1.4.3、修改geowebcache-servlet.xml 1.5、配置切片信息…

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

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

【自动化Selenium】Python 网页自动化测试脚本(上)

目录 1、Selenium介绍 2、Selenium环境安装 3、创建浏览器、设置、打开 4、打开网页、关闭网页、浏览器 5、浏览器最大化、最小化 6、浏览器的打开位置、尺寸 7、浏览器截图、网页刷新 8、元素定位 9、元素交互操作 10、元素定位 &#xff08;1&#xff09;ID定位 &…

微软Ignite 2024:建立一个Agentic世界!

在今年的Microsoft Ignite 2024上&#xff0c;AI Agent无疑成为本次大会的重点&#xff0c;已经有十万家企业通过Copilot Studio创建智能体了。微软更是宣布&#xff1a;企业可以在智能体中&#xff0c;使用Azure目录中1800个LLM中的任何一个模型了&#xff01; 建立一个Agent…

嵌入式linux系统中图像处理基本方法

目录 2.1 BMP图像处理 2.1.1 BMP文件格式解析 2.1.2 代码实现:将BMP文件解析为RGB格式,在LCD上显示 2.2 JPEG图像处理 2.2.1 JPEG文件格式和libjpeg编译 2.2.2 libjpeg接口函数的解析和使用 2.2.3 使用libjpeg把JPEG文件解析为RGB格式,在LCD上显示 …

探索 GAN 的演变之路

2014 年&#xff0c;在论文Generative Adversarial Networks中&#xff0c;首次提出了 GAN&#xff0c;其核心思想是“生成”与“对抗”。GAN 由一个生成器 G(Generator)和一个判别器 D(Discriminator)构成&#xff0c;前者用于捕捉数据分布&#xff0c;后者用于判别某个样本是…

Vue实训---5-路由搭建

回顾之前的代码 我们在my-vue-project\src\router\index.js中的代码如下&#xff1a; // 什么是路由&#xff1f;路由就是url地址和组件的对应关系 // 1.引入vue-router import { createRouter, createWebHashHistory } from vue-router// 2.定义路由 const routes [{path: …

【GAMES101笔记速查——Lecture 19 Cameras,Lenses and Light Fields】

本章节内容&#xff1a;相机、棱镜、光场 计算机图形学的两种成像方法&#xff1a; 1.合成方法&#xff1a;光栅化、光线追踪&#xff08;展示出现实没有的东西&#xff09; 2.捕捉方法&#xff1a;相机&#xff08;捕捉现实已有的东西&#xff09; 目录 1 相机 1.1 针孔相…

MacOS系统上Jmeter 录制脚本遇到的证书坑位

一、JMeter介绍与安装 1&#xff0c;下载及安装 jmeter官网地址 二、录制百度链接https请求时&#xff0c;需要导入jmeter相关证书到macos系统的更目录中. 导入方式&#xff0c;直接拖入mac的系统中&#xff0c;始终新人就可以&#xff1b; 三、jmeter 创建相关的录制组件…

软件团队的共担责任

问责制被认为是个人与其社会系统之间的纽带&#xff0c;它创造了一种将个人与其行为和绩效联系起来的身份关系。在入门系列的第一篇文章《超越工具和流程&#xff1a;成功软件开发团队的策略》中&#xff0c;我们介绍了问责制的概念&#xff0c;并提出了以下定义&#xff1a; …

【Python爬虫实战】深入解析 Scrapy:从阻塞与非阻塞到高效爬取的实战指南

&#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 目录 前言 一、阻塞和非阻塞 &#xff08;一&#xff09;阻塞 &#xff08;二&#xff09;非阻塞 二、Scrapy的工作…

【Python数据分析五十个小案例】电影评分分析:使用Pandas分析电影评分数据,探索评分的分布、热门电影、用户偏好

博客主页&#xff1a;小馒头学python 本文专栏: Python数据分析五十个小案例 专栏简介&#xff1a;分享五十个Python数据分析小案例 在现代电影行业中&#xff0c;数据分析已经成为提升用户体验和电影推荐的关键工具。通过分析电影评分数据&#xff0c;我们可以揭示出用户的…

第八篇:CamX RawHdr Feature Enable

CamX RawHdr Feature Enable RawHdr feature介绍: 试用于拍照场景,输入3张Raw,输出一张Raw。 对应的pipeline: camxSWMFMergeRaw.xml (usecases: UsecaseZSL) featureGraph: RTRawHDRBayer2YUVJPEG ​ RT -> RawHdr -> Bayer2Yuv -> JPEG RTRawHDRBayer2YUVJPE…

Python毕业设计选题:基于django+vue的期货交易模拟系统的设计与实现

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 系统首页 期货信息 个人中心 管理员登录界面 管理员功能界面 用户管理 期货公司管理…