C语言文件操作入门

本节重点

  • 理解文件的形式与基本概念
  • 二进制文件与文本文件
  • 文件的打开与关闭
  • 文件读写函数、
  • 文件缓冲区

正文开始---------------------------------------------------------------------------------------------------------------------

一、为什么使用文件

程序运行时数据存储在内存中(如变量、数组),但程序结束后内存会被操作系统回收,数据随之丢失,用户配置、日志记录、程序状态等数据需跨会话保存,文件是操作系统提供的标准持久化方案,通过文件存储,C 程序能够突破内存限制,实现数据的长期保存和跨会话共享,是构建健壮应用程序的基础能力。

也就是说数据保存在内存之中容易丢失,我们可以通过文件操作将数据保存在文件也就是磁盘或硬盘之中。

二、什么是文件

在计算机领域,文件是存储数据的核心单位,用于将信息以特定格式保存在物理存储设备(如硬盘、SSD)中。

在Windows中通过可视化操作界面,文件的存在形式是这样的:

而在Linux操作系统中,通过命令行操作文件的存在形式是这样的:

2.1 程序文件

程序文件是包含机器语言指令(二进制代码)或解释型语言代码的文件,可被操作系统识别并执行从而实现特定的功能。

程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行文件(Windows环境后缀为.exe)。

2.2 数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

在这里,我们主要讨论的是数据文件。

在之前我们所处理的数据的输入输出都是以终端为对象,即从终端的键盘输入数据(主要通过scanf 函数),运行结果显示到显示器上(主要通过 printf 函数)。

其实有时候我们可以把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上的文件。

2.3、文件名

文件名要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径+文件主干+文件后缀,例如:

D:\code\new_code\code.exe

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

四、二进制文件与文本文件

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。

数据在内存中以二进制的形式存储,如果不加转换就输出到外存的文件中,就是二进制文件。如果要求在外存上以ASCLL码的形式存储,则需要在存储前转换,以ASCLL码的形式存储的文件就是文本文件。

一个数据在文件中是怎么存储的呢?

字符一律按ASCLL码的形式存储,数值型数据既可以用ASCLL码形式存储也可以以二进制形式存储。

例如,一个数值型数据10000以ASCLL码存储时一共有5个字符分别为1、0、0、0、0共占用5个字节,而以二进制形式存储时占4个字节。

四、文件的打开与关闭

4.1 流和标准流

4.1.1 流

在编程中,“流”(Stream)是一种抽象的数据处理模型,用于逐步传输或处理数据,而无需一次性将整个数据加载到内存中。它类似于现实世界中的“水流”,数据像水流一样按顺序流动,程序可以按需读取或写入数据片段。

C程序针对文件、画面、键盘等的数据的输入输出操作都是通过流实现的。

4.1.2 标准流

在编程中,“标准流”(Standard Streams)是操作系统或运行时环境预定义的三个默认数据流,用于程序与外界(如终端、文件或其他程序)进行交互。它们是:

  1. stdin  -标准输入流,在大多数情况下从键盘输入,scanf函数就是从标准输入流中读取数据
  2. stdout-标准输出流,大多数环境中输出到显示器界面,printf函数就是将信息输出到标准输出流中
  3. stderror-标准错误流,大多数环境中输出到显示器界面。

当我们的程序启动,或者说进程创建时,操作系统会为我们的程序自动分配三个标准流并默认连接到终端设备(显示器、键盘)。

stdin、stdout、stderror三个流的类型都是FILE*,通常称为文件指针,在C语言中就是通过文件指针来维护流的各种操作的

4.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*)的概念。通过使文件指针指向某个文件的“文件信息区”(本质是一个结构体变量),通过该文件信息区中的信息就能找到并管理该文件。

也就是说,通过文件指针变量就能间接找到与它关联的文件。

4.3 文件的打开与关闭

文件在读写之前首先应该打开文件,在使用结束后关闭文件。本质上就是保证打开文件后系统就会创建对应的“文件信息区”来方便用户对文件进行管理与维护,关闭文件目的是释放相应的内存空间,不然过多无效的“文件信息区”会占用内存,严重时会导致内存泄漏。

在编写程序的时候,在打开文件的同时,都会返回一个FILE* 类型的指针变量指向该文件,也相当于建立了指针与文件的关系。

ANSIC规定使用fopen函数来打开文件,fclose来关闭文件:

//打开文件
FILE* fopen(const char* filename,const char* mode);//关闭文件
int fclose(FILE* stream);

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

模式描述读权限写权限追加模式文件不存在时文件存在时初始位置
r只读模式(文本文件)✔️失败(返回 NULL保留内容,从文件开头读取文件开头
w只写模式(文本文件,覆盖原有内容✔️创建新文件清空文件内容文件开头
a追加模式(文本文件,写入内容到文件末尾)✔️✔️创建新文件保留内容,写入位置在文件末尾文件末尾
r+读写模式(文本文件,不截断文件✔️✔️失败保留内容,从文件开头读写文件开头
w+读写模式(文本文件,覆盖原有内容✔️✔️创建新文件清空文件内容文件开头
a+读写追加模式(文本文件,写入到文件末尾,可读取整个文件)✔️✔️✔️创建新文件保留内容,写入位置在文件末尾文件末尾(读从开头)
rb只读模式(二进制文件)✔️失败保留内容,从文件开头读取文件开头
wb只写模式(二进制文件,覆盖原有内容✔️创建新文件清空文件内容文件开头
ab追加模式(二进制文件,写入内容到文件末尾)✔️✔️创建新文件保留内容,写入位置在文件末尾文件末尾
r+b读写模式(二进制文件,不截断文件✔️✔️失败保留内容,从文件开头读写文件开头
w+b读写模式(二进制文件,覆盖原有内容✔️✔️创建新文件清空文件内容文件开头
a+b读写追加模式(二进制文件,写入到文件末尾,可读取整个文件)✔️✔️✔️创建新文件保留内容,写入位置在文件末尾文件末尾(读从开头)
x独占创建模式(仅当文件不存在时创建,失败返回 NULL✔️/❌✔️创建新文件失败(文件已存在)文件开头

实例代码:

#include<stdio.h>int main()
{FILE* pf=fopen("text.txt","w");if(pf==NULL){//文件打卡失败perror("fopen");return 0;}const char* txt="hello world!!";fputs(txt,pf);fclose(pf);return 0;
}

五、文件的顺序读写

5.1  字符读写

fgetc 

  • 功能:从文件中读取单个字符
  • 参数:FILE* stream(文件指针)
  • 返回值:成功返回字符的int值,失败或文件结束返回EOF

fputc

  • 功能:向文件中写入单个字符
  • 参数:int c(字符),FILE* stream(文件指针)
  • 返回值: 成功返回写入的字符,失败返回EOF

5.2  字符串读写

fgets

  • 功能:从文件中读取一行字符串(直到遇到换行符或指定长度)
  • 参数:char* s(自定义缓冲区),int size(最大长度),FILE* stream(文件指针)
  • 返回值:成功返回缓冲区的指针,失败或文件结束返回NULL

fputs

  • 功能:向指定文件中写入一行字符串
  • 参数:const char* s(字符串),FILE* stream
  • 返回值:成功返回非负值,失败返回EOF

5.3  格式化读写

fscanf

  • 功能:按指定格式从文件中读取数据
  • 参数:FILE* stream,格式化字符串,变量地址列表
  • 返回值:成功匹配并赋值的参数个数,失败返回EOF

fprintf

  • 功能:按指定格式向文件中写入数据
  • 参数:FILE* stream,格式化字符串,变量列表
  • 返回值:成功返回写入的字符数,失败返回负数

5.4  块读写(二进制模式)

fread

  • 功能:从文件中读取二进制数据块
  • 参数:void* ptr(缓冲区),size_t size(每个元素大小),size_t nmemb(要读取的元素数量),FILE* stream。

  • 返回值:成功读取的元素数量。

fwrite

  • 功能:向文件写入二进制数据块。
  • 参数:void* ptr(要写入的数据的指针),size_t size(每个元素大小)size_t nmemb(要写入的元素的数量)FILE *stream

  • 返回值:成功写入的元素数量

六、文件的随机读写 

文件的随机读写允许程序直接跳转到文件的任意位置进行读写操作,无需按顺序访问。

fseek

  • 功能:移动文件指针到指定位置
  • 参数:FILE* stream(文件指针),long offset(偏移量字节) ,int origin(基准位置)
  • 返回值:成功返回0,失败返回非0值

ftell

  • 功能:返回文件指针相对于起始位置的偏移量
  • 参数:FILE* stream
  • 返回值: 成功返回long类型的偏移量

rewind

  • 功能:将文件指针重置到文件开头
  • 参数:FILE* stream
  • 返回值: 无

七、文件缓冲区

在之后的学习中我们会了解到,其实C语言中文件的各类读写函数其实底层都封装了系统调用接口read,write来向操作系统发出各类操作指令,而系统调用是一个复杂的概念它涉及到操作系统对内存空间的各类调度与管理,如果C语言中的文件函数直接访问文件或磁盘中的数据,频繁地进行读取和写入操作,就意味着系统调用接口的频繁调用加重CPU的负担,减低I/O的效率

为了避免这个问题,我们引入了文件缓冲区的概念,它通过先将程序数据先放入缓冲区中,通过刷新机制将多次小数据量的读写合并为少量大块操作,减少直接访问磁盘的次数,显著提升I/O效率,避免频繁的系统调用。

 写个代码验证一下:

#include<stdio.h>
#include<windows.h>
int main()
{FILE*pf = fopen("test.txt", "w");fputs("abcdef", pf);//先将代码放在输出缓冲区printf("睡眠10秒已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");Sleep(10000);printf("刷新缓冲区\n");fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)//注:fflush 在⾼版本的VS上不能使⽤了printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");Sleep(10000);fclose(pf);//注:fclose在关闭⽂件的时候,也会刷新缓冲区pf = NULL;return 0
}

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

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

相关文章

Doris:联邦认证

LDAP​ 接入第三方 LDAP 服务为 Doris 提供验证登录和组授权服务。 LDAP 验证登录​ LDAP 验证登录指的是接入 LDAP 服务的密码验证来补充 Doris 的验证登录。Doris 优先使用 LDAP 验证用户密码&#xff0c;如果 LDAP 服务中不存在该用户则继续使用 Doris 验证密码&#xff…

stm32第六天继电器

一&#xff1a;继电器 1.继电器的工作原理 继电器是一个电控开关&#xff0c;工作原理基于电磁感应&#xff0c;继电器包括一个电磁线圈和一组触点。常用于控制高电流或高电压的电路&#xff0c;例如自动控制原理&#xff0c;电力系统和自动化设备中&#xff0c;由于可靠性和电…

Vue渲染函数 - render 函数

文章目录 Vue渲染函数 - render 函数1. 什么是 render 函数2、页面展示过程3、render 函数的参数4. 如何使用&#xff08;1&#xff09;基本渲染&#xff08;2&#xff09;传递属性和事件&#xff08;3&#xff09;条件渲染 5. render 函数的实际使用6.View Design 组件中的使用…

单片机自学总结

自从工作以来&#xff0c;一直努力耕耘单片机&#xff0c;至今&#xff0c;颇有收获。从51单片机&#xff0c;PIC单片机&#xff0c;直到STM32&#xff0c;以及RTOS和Linux&#xff0c;几乎天天在搞:51单片机&#xff0c;STM8S207单片机&#xff0c;PY32F003单片机&#xff0c;…

go回调函数的使用

在Go语言中&#xff0c;回调函数可以有参数&#xff0c;也可以没有参数。它们的定义和使用方式略有不同&#xff0c;但本质上都是将函数作为参数传递给另一个函数&#xff0c;并在适当的时候调用它。以下是带参数和不带参数的回调函数的示例和说明。 1. 不带参数的回调函数 不…

在 Ubuntu 中配置 NFS 共享服务的完整指南

前言 网络文件系统&#xff08;NFS&#xff09;作为 Linux 系统间实现文件共享的标准协议&#xff0c;在分布式计算和容器化部署场景中具有重要作用。本文将详细演示如何在 Ubuntu 系统上配置 NFS 服务端与客户端&#xff0c;并实现可靠的持久化挂载。 一、环境准备 系统要求…

TypeScript Symbols 深度解析:在 Vue3 中的高级应用实践

一、Symbols 核心特性解析 1.1 什么是 Symbol&#xff1f; Symbol 是 ES6 引入的原始数据类型&#xff0c;表示唯一且不可变的值&#xff0c;主要解决对象属性名冲突问题。在 TypeScript 中&#xff0c;我们通过 symbol 类型获得完整的类型支持&#xff1a; const SERIAL_KE…

无需刷机、root,畅享原生安卓的丝滑体验。

Apex Launcher 是一款历史悠久的 Android 桌面启动器&#xff0c;诞生于 Android 系统早期&#xff08;Android 4.0 时代&#xff09;。当时&#xff0c;Android 系统的默认界面被认为较为简陋&#xff0c;无法满足一些追求个性化和高效操作的用户需求。因此&#xff0c;许多开…

Visual Studio Code安装配置优化全攻略:打造高效开发环境

目录 一、背景与意义 二、安装与配置基础 2.1 下载与安装 2.2 核心配置目录 三、深度优化配置指南 3.1 主题与界面优化 3.2 必装效率插件&#xff08;精选TOP10&#xff09; 3.3 性能优化设置 四、实战案例&#xff1a;前端开发环境配置 4.1 项目初始化 4.2 调试配置…

味觉传送器E-Taste:开启虚拟世界的味觉之门

味觉传送器E-Taste&#xff1a;开启虚拟世界的味觉之门 一、发明背景与动机 随着虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;技术的飞速发展&#xff0c;人们在虚拟世界中的沉浸感不断提升&#xff0c;视觉和听觉体验已经取得了显著的突破。然而…

判断质数与合数

判断质数与合数的逻辑很相似&#xff0c;都是判断一个属除了1和它本身&#xff0c;能不能被其他数整除。 其他数包括质数与合数&#xff0c;合数能表示能质数的乘积&#xff0c;因此问题就转化为&#xff1a;一个数能不能被除了1和它本身之外的其他质数整除。 质数2&#xff…

在Spring Boot项目中接入DeepSeek深度求索,感觉笨笨的呢

文章目录 引言1. 什么是DeepSeek&#xff1f;2. 准备工作2.1 注册DeepSeek账号 3.实战演示3.1 application增加DS配置3.2 编写service3.3 编写controller3.4 编写前端界面chat.html3.5 测试 总结 引言 在当今快速发展的数据驱动时代&#xff0c;企业越来越重视数据的价值。为了…

Cursor在内网环境配置自定义DeepSeek API

关键字 Cursor、DeepSeek、API配置、内网代理、HTTP/2 背景环境 使用Cursor集成环境开发程序。但是我使用公司的内网并不能使用cursor自带的模型&#xff0c;于是我就想使用DeepSeek官方的API服务。 环境&#xff1a;Windows 11系统 解决过程 网络检测 首先进行环境检测&am…

RabbitMQ 集群降配

这里写自定义目录标题 摘要检查状态1. 检查 RabbitMQ 服务状态2. 检查 RabbitMQ 端口监听3. 检查 RabbitMQ 管理插件是否启用4. 检查开机自启状态5. 确认集群高可用性6. 检查使用该集群的服务是否做了断开重连 实操1. 负载均衡配置2. 逐个节点降配&#xff08;滚动操作&#xf…

设计模式之外观模式:原理、实现与应用

引言 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它通过提供一个统一的接口来简化复杂系统的使用。外观模式隐藏了系统的复杂性&#xff0c;使得客户端可以通过一个简单的接口与系统交互。本文将深入探讨外观模式的原理、实现方式以及实…

进行交通流预测,使用KAN+Transformer模型

理论基础 KAN&#xff08;Knowledge Augmented Network&#xff09; KAN 是一种知识增强网络&#xff0c;其核心思想是将先验知识融入到神经网络中&#xff0c;以此提升模型的性能与泛化能力。在交通流预测领域&#xff0c;先验知识可以是交通规则、历史交通模式等。通过把这…

TF中 Arg 节点

TF中 Arg 节点 在 TensorFlow 的计算图中&#xff0c;_Arg 节点&#xff08;Argument Node&#xff09;表示函数的输入参数&#xff0c;是计算图中负责接收外部输入数据的节点。它的名字来源于“Argument”&#xff08;参数&#xff09;&#xff0c;直接对应函数调用时传入的张…

Educational Codeforces Round 176 (Rated for Div. 2)

A.To Zero 签到题 void solve() { int n,k;cin>>n>>k;int k2k/2*2;int k1(k2<k)?k:k-1;int cnt0;if(n%21){n-k1;cnt;cnt(n/k2)(n%k2!0);}else {cnt(n/k2)(n%k2!0);}cout<<cnt<<endl;}B.Array Recoloring 手推一下可以发现&#xff0c;答案其实就…

Kubernetes的Service详解

一、Service介绍 在 kubernetes 中&#xff0c; pod 是应用程序的载体&#xff0c;我们可以通过 pod 的 ip 来访问应用程序&#xff0c;但是 pod 的 ip 地址不是固定的&#xff0c;这也就意味着不方便直接采用pod 的 ip 对服务进行访问。 为了解决这个问题&#xff0c;kuberne…

基于Nvidia Jetson Nano边缘计算设备使用TensorRT部署YOLOv8模型实现目标检测推理

0、背景 最近拿到一台边缘计算设备&#xff0c;在部署YOLO模型的过程中遇到一些问题&#xff0c;特此记录。 设备介绍信息&#xff1a;NVIDIA Jetson Orin Nano T201Developer Kit 开发套件 开发者套件&#xff1a;Jetson Orin Nano T201 8GB开发套件 使用指南文档&#x…