【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.mzph.cn/pingmian/61944.shtml

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

相关文章

从 0 到 1 掌握部署第一个 Web 应用到 Kubernetes 中

文章目录 前言构建一个 hello world web 应用项目结构项目核心文件启动项目 检查项目是否构建成功 容器化我们的应用编写 Dockerfile构建 docker 镜像推送 docker 镜像仓库 使用 labs.play-with-k8s.com 构建 Kubernetes 集群并部署应用构建 Kubernetes 集群环境编写部署文件 总…

Matlab以一个图像分类例子总结分类学习的使用方法

目录 前言 导入数据 训练学习 导出训练模型 仿真测试 总结 前言 最近在尝试一些基于Simulink的边沿AI部署,通过这个案例总结Matlab 分类学习功能的使用。本案例通过输入3000张28*28的灰度图像,训练分类学习模型。并验证训练好的模型最后部署到MCU。 导入数据 如下图是…

2025蓝桥杯(单片机)备赛--扩展外设之UART1的原理与应用(十二)

一、串口1的实现原理 a.查看STC15F2K60S2数据手册: 串口一在590页&#xff0c;此款单片机有两个串口。 串口1相关寄存器&#xff1a; SCON:串行控制寄存器&#xff08;可位寻址&#xff09; SCON寄存器说明&#xff1a; 需要PCON寄存器的SMOD0/PCON.6为0&#xff0c;使SM0和SM…

Reactor 模式的理论与实践

1. 引言 1.1 什么是 Reactor 模式&#xff1f; Reactor 模式是一种用于处理高性能 I/O 的设计模式&#xff0c;专注于通过非阻塞 I/O 和事件驱动机制实现高并发性能。它的核心思想是将 I/O 操作的事件分离出来&#xff0c;通过事件分发器&#xff08;Reactor&#xff09;将事…

【Android+多线程】IntentService 知识总结:应用场景 / 使用步骤 / 源码分析

定义 IntentService 是 Android中的一个封装类&#xff0c;继承自四大组件之一的Service 功能 处理异步请求 & 实现多线程 应用场景 线程任务 需 按顺序、在后台执行 最常见的场景&#xff1a;离线下载不符合多个数据同时请求的场景&#xff1a;所有的任务都在同一个T…

Easy Excel 通过【自定义批注拦截器】实现导出的【批注】功能

目录 Easy Excel 通过 【自定义批注拦截器】实现导出的【批注】功能需求原型&#xff1a;相关数据&#xff1a;要导出的对象字段postman 格式导出对象VO 自定义批注拦截器业务代码&#xff1a; 拦截器代码解释&#xff1a;详细解释&#xff1a;格式优化&#xff1a; Easy Excel…

Spring Boot 的 WebClient 实践教程

什么是 WebClient&#xff1f; 在 Spring Boot 中&#xff0c;WebClient 是 Spring WebFlux 提供的一个非阻塞、响应式的 HTTP 客户端&#xff0c;用于与 RESTful 服务或其他 HTTP 服务交互。相比于传统的 RestTemplate&#xff0c;WebClient 更加现代化&#xff0c;具有异步和…

QML学习 —— 29、3种不同使用动画的方式(附源码)

效果 说明 第一种:属性动画 - 当启动软件时候自动执行动画。      第二种:行为动画 - 当属性发生变化则自动执行动画。      第三种:目标动画 - 将动画变为对象,指定对象的目标进行执行动画。 代码 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQu…

Redis缓存穿透及常见的解决方案

一.什么是缓存穿透&#xff1f; 缓存穿透是指当客户端请求的数据在缓存&#xff08;如 Redis&#xff09;中不存在&#xff0c;并且在数据库中也不存在时&#xff0c;直接绕过缓存去请求数据库。这种情况会导致&#xff1a; 缓存系统无法发挥作用&#xff0c;数据每次都会直接…

(原创)Android Studio新老界面UI切换及老版本下载地址

前言 这两天下载了一个新版的Android Studio&#xff0c;发现整个界面都发生了很大改动&#xff1a; 新的界面的一些设置可参考一些博客&#xff1a; Android Studio新版UI常用设置 但是对于一些急着开发的小伙伴来说&#xff0c;没有时间去适应&#xff0c;那么怎么办呢&am…

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

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

Node.js的http模块:创建HTTP服务器、客户端示例

新书速览|Vue.jsNode.js全栈开发实战-CSDN博客 《Vue.jsNode.js全栈开发实战&#xff08;第2版&#xff09;&#xff08;Web前端技术丛书&#xff09;》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) 要使用http模块&#xff0c;只需要在文件中通过require(http)引入即可。…

AI赋能电商:构建高效、智能化的新零售生态

随着人工智能&#xff08;AI&#xff09;技术的不断进步&#xff0c;其在电商领域的应用日益广泛&#xff0c;从购物推荐到供应链管理&#xff0c;再到商品定价&#xff0c;AI正在全面改变传统电商的运营模式&#xff0c;并推动行业向智能化和精细化方向发展。本文将探讨如何利…

算法之区间和题目讲解

题干 难度&#xff1a;简单 题目分析 题目要求算出每个指定区间内元素的总和。 然而&#xff0c;区间在输入的最下面&#xff0c;所以按照暴力破解的思路&#xff0c;我们首先要遍历数组&#xff0c;把它的值都存进去。 然后&#xff0c;遍历下面的区间&#xff0c;从索引a…

openssl颁发包含主题替代名的证书–SAN

原文地址&#xff1a;openssl颁发包含主题替代名的证书–SAN – 无敌牛 欢迎参观我的个人博客&#xff1a;无敌牛 – 技术/著作/典籍/分享等 在 X.509 证书中&#xff0c;commonName&#xff08;CN&#xff09;字段只能有一个值。如果让证书支持多个域名和IP地址&#xff0c;…

从尾到头打印链表 剑指offer

题目描述 输入一个链表的头节点&#xff0c;从尾到头反过来打印出每个节点的值。 链表节点定义如下&#xff1a; struct ListNode {int m_nKey;ListNode*m_pNext; }; 代码实现 栈实现&#xff1a; 递归实现&#xff1a; 但是用递归实现可能存在的问题&#xff1a;

ajax基础

一&#xff1a;express框架 在终端输入nodejs文件名 // 引入express const express require(express); //创建应用对象 const app express(); //创建路由规则 app.get(/,(request,response) > {//设置响应response.send(Hello Express); }); // 监听3000端口 app.lis…

免费实用在线AI工具集合 - 加菲工具

免费在线工具-加菲工具 https://orcc.online/ 在线录屏 https://orcc.online/recorder 时间戳转换 https://orcc.online/timestamp Base64 编码解码 https://orcc.online/base64 URL 编码解码 https://orcc.online/url Hash(MD5/SHA1/SHA256…) 计算 https://orcc.online/h…

UE5肉鸽游戏教程学习

学习地址推荐&#xff1a;UE5肉鸽项目实战教程_哔哩哔哩_bilibili

101页PDF | 德勤_XX集团信息化顶层规划设计信息化总体解决方案(限免下载)

一、前言 这份报告是一份关于集团信息化顶层规划设计的总体解决方案&#xff0c;旨在通过信息化转型提升集团管控和企业运营效率。报告回顾了项目的背景、目标和工作过程&#xff0c;分析了集团面临的内部和外部挑战&#xff0c;并提出了一系列解决方案&#xff0c;包括自上而…