C语言文件操作,文件读写

目录

为什么要使用文件?

文件概念

1. 什么是文件?

2. 程序文件

3. 数据文件

4. 文件名

文件的使用

1. 文件指针

2. 文件的打开与关闭

文件的顺序读写 

1. 顺序读写函数

2. scanf系列与printf系列

文件的随机读写 

1. fseek

2. ftell

3. rewind

文本文件,二进制文件 

文件读取结束的判定 

文件缓冲区


为什么要使用文件?

我们前面学习结构体时,写通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。
我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。
这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件或存放到数据库等方式。
使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。


文件概念

1. 什么是文件?

1. 文件是指磁盘上的文件。

2. 在以前学习中所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处 的就是磁盘上文件。

3. 但是在程序设计中,我们一般谈的文件有两种,是从文件功能的角度来分类的:程序文件、数据文件。

2. 程序文件

1. 源文件(后缀为.c)。

2. 目标文件(windows环境下后缀为.obj)。

3. 可执行程序(windows环境下后缀为.exe)。

3. 数据文件

程序运行需要从中读取数据的文件,或者输出内容的文件。

4. 文件名

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

2. 为了方便起见,文件标识常被称为文件名。

3. 文件名包含3部分:文件路径+文件名主干+文件后缀,例如: c:\code\ + test + .txt


文件的使用

1. 文件指针

1. 文件指针是指文件类型的指针。

2. 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。

3. 这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。

struct _iobuf {char *_ptr;int _cnt;char *_base;int _flag;int _file; int _charbuf; int _bufsiz;char *_tmpfname;};typedef struct _iobuf FILE;

4. 不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。

5. 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。

FILE* pf; //文件指针变量

6. 定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。

2. 文件的打开与关闭

1. 文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。

打开文件

FILE* fopen(const char* filename, const char* mode);

关闭文件

int fclose(FILE* stream);

2. 打开模式(mode)如下:

.

3. 例子

int main ()
{FILE* pFile;//打开文件pFile = fopen("myfile.txt","w");//文件操作if (pFile!=NULL){fputs("fopen example",pFile);//关闭文件fclose(pFile);}return 0;
}

文件的顺序读写 

1. 顺序读写函数

.

1. 将一个字符写入流中。

int fputc(int character, FILE* stream);//例子
int main ()
{FILE* pFile = fopen("myfile.txt","w");fputc('a', pFile);  fclose(pFile);return 0;
}

.

2. 从流中获取一个字符。

int fgetc(FILE* stream);//例子
int main ()
{FILE* pFile = fopen("myfile.txt","r");int ch = fgetc(pFile);  printf("%c\n", ch);fclose(pFile);return 0;
}

读的原理:文件打开,文件指针默认指向起始位置,读一个之后文件指针就往后一个位置。

.

3. 读与写的理解

这里我们的主语(主视角)始终是程序。

.

4. 将字符串写入流

int fputs(const char* str, FILE* stream);//例子
int main()
{FILE* pFile = fopen("myfile.txt", "w");fputs("abc\n", pFile);fclose(pFile);pFile = NULL;return 0;
}

.

5. 从流中获取字符串

char* fgets(char* str, int num, FILE* stream);

读num-1个,或者遇到换行,或者到文件末尾停下。

int main()
{FILE* pFile = fopen("myfile.txt", "r");char str[10];fgets(str, 10, pFile);printf("%s\n", str);fclose(pFile);pFile = NULL;return 0;
}

.

6. 将格式化数据写入流

int fprintf(FILE* stream, const char* format, ...);

例子

struct S
{int i;float f;
}s = {1, 3.1f};int main()
{FILE* pFile = fopen("myfile.txt", "w");fprintf(pFile, "%d, %f", s.i, s.f);fclose(pFile);pFile = NULL;return 0;
}

.

7. 从流中读取格式化数据

int fscanf(FILE* stream, const char* format, ...);

例子

struct S
{int i;float f;
}s = { 0 };int main()
{FILE* pFile = fopen("myfile.txt", "r");fscanf(pFile, "%d, %f", &(s.i), &(s.f));printf("%d, %f", s.i, s.f);fclose(pFile);pFile = NULL;return 0;
}

.

8. 将数据块写入流(二进制输出)

size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);

从 ptr 指向的空间开始拷贝 count 个大小为 size 的数据到 stream 流中。

struct S
{int i;float f;
};int main()
{FILE* pf = fopen("data.txt", "wb");if (pf == NULL){perror("fopen");return 1;}struct S s = { 1, 1.0 };fwrite(&s, sizeof(s), 1, pf);fclose(pf);pf = NULL;return 0;
}

因为是二进制文件,所以用文本文件打开就会乱码。

.

9. 从流中读取数据块(二进制输入)

size_t fread(void* ptr, size_t size, size_t count, FILE* stream);

从 stream 流中读 count 个大小为 size 的数据放到 ptr 所指向的空间。 

struct S
{int i;float f;
};int main()
{FILE* pf = fopen("data.txt", "rb");if (pf == NULL){perror("fopen");return 1;}struct S s;fread(&s, sizeof(s), 1, pf);printf("%d, %f\n", s.i, s.f);fclose(pf);pf = NULL;return 0;
}

2. scanf系列与printf系列

printf系列

1. 将格式化数据打印到 stdout

int printf(const char* format, ...);

2. 将格式化数据写入流

int fprintf(FILE* stream, const char* format, ...);

3. 将格式化数据写入字符串

int sprintf(char* str, const char* format, ...);

scanf系列

1. 从 stdin 读取格式化数据

int scanf(const char* format, ...);

2. 从流中读取格式化数据

int fscanf(FILE* stream, const char* format, ...);

3. 从字符串中读取格式化数据

int sscanf(const char* s, const char* format, ...);

sprintf与sscanf的例子

struct S
{int i;float f;
};int main()
{char arr[20];struct S s = { 1, 1.0 };sprintf(arr, "%d, %f", s.i, s.f);printf("arr:%s\n", arr);struct S tmp;sscanf(arr, "%d, %f", &(tmp.i), &(tmp.f));printf("tmp:%d, %f\n", tmp.i, tmp.f);return 0;
}


文件的随机读写 

1. fseek

int fseek(FILE* stream, long int offset, int origin);

1. 根据文件指针的位置和偏移量来定位文件指针。

2. 控制 stream 文件指针从 origin 开始偏移 offset。 

3. 文件指针默认在文件起始位置,origin有三个选项控制文件指针的起始位置。

例子

文件指针默认指向起始位置也就a,我想访问c。

int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}fseek(pf, -2, SEEK_END); printf("%c\n", fgetc(pf));fclose(pf);pf = NULL;
}

2. ftell

long int ftell(FILE* stream);

返回文件指针相对于起始位置的偏移量。

int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}printf("%c\n", fgetc(pf));printf("%c\n", fgetc(pf));printf("%c\n", fgetc(pf));printf("%d\n", ftell(pf));fclose(pf);pf = NULL;
}

3. rewind

void rewind(FILE* stream);

让文件指针的位置回到文件的起始位置。

int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}printf("%c\n", fgetc(pf));printf("%c\n", fgetc(pf));printf("%c\n", fgetc(pf));rewind(pf);printf("%c\n", fgetc(pf));fclose(pf);pf = NULL;
}


文本文件,二进制文件 

1. 根据数据的组织形式,数据文件被分为文本文件与二进制文件。

2. 数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。

3. 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

.

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

1. 字符一律以ASCII形式存储。

2. 数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

3. 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节。

二进制编辑器打开


文件读取结束的判定 

1. 文本文件读取结束的判断

用 fgetc 判断返回值是否为 EOF 或用 fgets 判断返回值是否为 NULL。

.

2. 二进制文件读取结束的判断

用 fread 判断返回值是否小于实际要读的个数。

.

3. 正确使用 feof

在文件读取过程中,不能用 feof 函数的返回值直接来判断文件的是否结束。

feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是遇到文件末尾结束。


文件缓冲区

1. 系统会自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。

2. 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。

3. 如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输到内存缓冲区,充满缓冲区后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。

4. 缓冲区的大小根据编译器决定的。

.

这样做的原因:

1. 当我们使用读写函数的时候,这些函数底层会调用操作系统的接口,让操作系统帮我们读写数据。

2. 实际上操作系统运行着很多进程,如果我们频繁使用这些函数,就会频繁的打断操作系统,这样操作系统就罢工了。

3. 所以我们设一个缓冲区,放满了才去麻烦操作系统。

.

例子

结论:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

B端:用弹框还是用抽屉,请说出你的依据。

选择浮层(弹出框)还是抽屉(侧边栏)作为B端系统的浮层,需要根据具体情况来决定。以下是一些依据供您参考: 1.功能需求: 浮层的选择应该符合系统的功能需求。如果需要在当前页面上提供一些额外的操…

C++ 基础(类和对象下)

目录 一. 再探构造函数 1.1. 初始化列表(尽量使用列表初始化) 二. static成员 2.1static成员初始化 三.友元 3.1友元:提供了⼀种 突破类访问限定符封装的方式. 四.内部类 4.1如果⼀个类定义在另⼀个类的内部,这个内部类就叫…

Google Android 2024年7月最新消息汇总

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点 Google Android 2024年7月最新消息汇总 2024年7月,Google在Android生态系统中发布了多项更新和政策调整,涵盖了Google …

6万字,让你轻松上手的大模型 LangChain 框架

本文为我学习 LangChain 时对官方文档以及一系列资料进行一些总结~覆盖对Langchain的核心六大模块的理解与核心使用方法,全文篇幅较长,共计50000字,可先码住辅助用于学习Langchain。** 一、Langchain是什么? 如今各类…

开源多协议分布式压力测试工具Tsung详解

目录 1、Tsung简介 2、Tsung安装 3、Tsung的使用流程 4、Tsung配置详解 4.1、客户端配置 4.2、服务端配置 4.3、压力阶段配置 4.4、选项参数配置 4.5、会话动作配置 5、Tsung压测后的结果分析 5.1、统计数据表 5.2、曲线图 6、其他 6.1、record录制 6.2、Plotte…

多区域DNS以及主从DNS的搭建

搭建多域dns服务器: 搭建DNS多区域功能(Multi-Zone DNS)主要是为了满足复杂网络环境下的多样化需求,提高DNS服务的灵活性、可扩展性和可靠性。 适应不同网络环境: 在大型组织、跨国公司或跨地域服务中,网…

【算法】单向环形链表解决Josephu(约瑟夫)问题

应用场景 n 个小孩标号,逆时针站一圈。从 k 号开始,每一次从当前的小孩逆时针数 m 个,然后让最后这个小孩出列。不断循环上述过程,直到所有小孩出列,由此产生出一个队列编号。 提示 用一个不带头节点的循环链表来处…

使用Dependency Walker和Process Explorer排查瑞芯微工具软件RKPQTool.exe启动报错的问题

目录 1、问题说明 2、使用Dependency Walker查看工具程序的库依赖关系 3、在可以运行的电脑上使用Process Explorer查看依赖的msvcr120.dll和msvcp120.dll库的路径 4、C/C++运行时库介绍 5、可以下载安装VC_redist.x86.exe或VC_redist.x64.exe解决系统库缺失问题 C++软件异…

Radon(拉当) 变换:超详细讲解(附MATLAB,Python 代码)

Radon 变换 Radon 变换是数学上用于函数或图像的一种积分变换,广泛应用于图像处理领域,尤其是在计算机断层成像 (CT) 中。本文档将详细介绍 Radon 变换的数学含义及其在图像处理中的应用。 数学定义 Radon 变换的数学定义是将二维函数 f ( x , y ) f…

vue 开发环境配置

1. nvm 安装 在 github上下载 最新的 nvm 包 https://github.com/coreybutler/nvm-windows/releases或者在 csdn 上下载(从github上迁移,方便下载)https://download.csdn.net/download/u011171506/89585197 下载后不用修改任何配置&#x…

提取视频中的文字如何提取?分享4种简单提取方法

在短视频时代,视频已成为信息传播的重要载体。然而,面对海量的视频资源,如何高效提取其中的文字信息,成为许多人关注的焦点,因为快速提取出视频中的文字可以帮助我们整理、编辑文本信息,下面给大家分享4种简…

构建现代化农业产业服务平台的系统架构

随着全球农业产业的发展和技术的进步,农业生产管理面临着越来越复杂的挑战和机遇。建立一个现代化的农业产业服务平台系统架构,不仅能够提高农业生产效率和管理水平,还能促进农民收入增长和可持续发展。本文将探讨如何设计和实施这样一个系统…

1.1 线性表顺序结构的定义及实现

1. 顺序表定义 把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。用这种方法存储的线性表简称顺序表。 2.顺序表特点 1)线性表的逻辑顺序与物理顺序一致; 2)数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现。设有…

将手机作为服务器运行docker服务

前言 目前手机的配置并不低,即使是2019年生产的一加七Pro,配置也有12256,CPU是骁龙855,作为服务器运行着配置绰绰有余了,二手的价格现在是400左右也能接受。相对于是自带ups电源的便携低耗docker服务器,还…

分布式事务和2阶段提交

在当今世界,数据正在以惊人的速度增长。单台计算机的存储容量是有限的,因此需要将数据分布在多台机器或数据库上,这种技术被称为分布式技术。 互联网大厂中很多系统都在采用微服务架构。在这种架构中,每个微服务都管理自己的数据…

NXP i.MX 6系列处理器加入“产品长期供货计划”

近期,NXP(恩智浦半导体)的i.MX 6系列处理器已加入其“产品长期供货计划”,不同型号处理器的生命周期得到了10~15年的延长,确保了长期稳定的供货与维护。 (NXP产品长期供货计划的目的,是给客户的…

pycharm关闭项目时,页面卡住了,怎么办?

问题 在关闭pycharm时,有时会遇到卡在退出进度条的界面,很讨厌,那我们要怎么办才能退出呢? 说明:本篇文章不是从根源上解决这个问题,无法避免这种情况。 解决方法 方法一: 在卡住时&#xf…

Redis 7.x 系列【27】集群原理之通信机制

有道无术,术尚可求,有术无道,止于术。 本系列Redis 版本 7.2.5 源码地址:https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2 节点和节点2.1 集群拓扑2.2 集群总线协议2.3 流言协议2.4 心跳机制2.5 节点握…

【推研小灶】复旦与南大之间:一次独特的计算机保研之旅

写在前面 上午10点填完志愿等待复试通知,利用这段时间记录一下我简短的夏令营和预推免。今年变为线下之后,部分学校的入营情况、考核方式有明显变化。加上CS方向保研名额总体变多,形势有点小乱,甚至填报系统都在9.29中秋节当天&a…