文件的操作

前言:哈喽小伙伴们好久不见,国庆假期已经结束,接下来我们还是要马不停蹄的投入到学习当中,只有尽快调整状态回归学习,才能弯道超车

今天我们一起来学习C语言——文件操作

本篇文章讲到的所有函数均需要头文件#include<stdio.h>。 


目录

一.什么是文件

1.程序文件

2.数据文件

3.文件名

二.为什么要使用文件

三.文件操作

1.文件指针

2.文件的打开与关闭

3.文件读写

(1)顺序读写

1)字符输入输出函数

2)文本行输入输出函数

3)格式化输入输出函数

4) 二进制输入输出函数

(2)随机读写

1)fseek

2)ftell

 3)rewind

四.结语


一.什么是文件

我们电脑上磁盘中的各种文件就是我们今天所要讲的文件。

 

对于我们程序猿来说,从文件的功能角度来分析,我们一般所使用的文件有两种:

  • 程序文件
  • 数据文件

1.程序文件

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

2.数据文件

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

那么本篇文章我们讲述的便是数据文件

3.文件名

一个文件要有一个唯一的文件标识,以便于识别和辨认。

文件名包含三部分:文件路径 + 文件名主干 + 文件后缀

例如:c:\project\test.txt

c:\project        表示文件的当前路径

test                  是文件名的主干

txt                    是文件的后缀

为了方便,我们就将文件标识称为文件名


二.为什么要使用文件

前边我们已经学习过了如何制作简易的通讯录,并讲解了如何通过动态内存函数来实现一个大小可变的通讯录

但是现在我们又有问题了,因为我们实现的通讯录并不能够真正的保存信息

当程序运行起来时,我们所输入的数据被保存在内存中,但是当我们退出程序时这些数据就会自动被内存所释放

所以如果想要保存这些数据,以便于我们下次打开通讯录时这些数据依然存在,就要用到文件了。


三.文件操作

1.文件指针

对于文件的相关使用,我们有文件指针这样一个定义。

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

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

同俗的来说,就是编译器系统内部已经帮你声明了一个结构体类型这个结构体类型就被命名为文件,当你要对文件进行操作时,只需要用到FILE这个类型名就行,就类似于库函数的使用

 那么想要真正实现对文件的操作,就需要一个FILE类型的指针:

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

通过这个指针来找到文件的信息区,并进行各种操作


2.文件的打开与关闭

我们想要操作一个文件,必然就需要先打开它。

那么对于文件的打开,我们有一个专门的函数:

该函数有两个参数:

filename       是文件名,字符串形式输入

mode            是文件的打开方式,字符串形式输入

此外,这个函数会返回一个FILE*类型的指针来指向我们打开的这个文件,这样便建立起了文件与指针的关系。 

既然有打开,那就一定会有关闭,关闭文件同样是通过一个函数:

这个函数只有一个参数:

stream        文件操作指针 

通过指向文件的操作指针来关闭文件。

那么文件都有哪些打开方式呢???

 这张表包含了所有的文件打开方式,下面我们就通过实际应用来讲解文件到底如何使用:

#include<stdio.h>
int main()
{//打开文件FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//操作文件//.....//关闭文件fclose(pf);pf = NULL;return 0;
}

fopen函数通过“w”(只写)文件的方式来打开一个名为“test.txt”的文件,并用pf指针来接收。

要注意的是,打开文件也是会出错的如果出错,就会返回NULL空指针,所以我们一定要进行一次判断,判断pf指针是否为空,为空则通过perror函数报错用return 1的方式来异常结束程序

操作完文件之后,我们要将文件关闭,这时候便用到了fclose函数

值得注意的是,文件关闭之后,pf指针就等于指向了一片未打开的文件信息区,这样pf就成为了野指针,所以要将pf指针及时置空

运行程序, 通过“w”(只写)方式来打开文件,如果我们前面已经仔细看过了文件打开方式表,就会发现表的最后一列表示,如果文件不存在的话,就会新建一个文件

此时如果你查看你的文件目录,就会发现我们创建了一个“test.txt”的文件。

如果我们一开始没有文件,想要通过“r”(只读)的方式来打开文件的话,就会报错:

很容易能够读出此英文含义为:没有这样的文件或者文件夹存在。 


我们上述所打开的文件,是在我们当前的编译器的工程文件夹中,那如果我想打开一个其他位置的文件怎么办呢???

  • 通过“..//”逐级往前
  • 直接通过文件的绝对路径

例如:

fopen("..\\test.txt","w");

就是打开我们当前工程文件夹的上一级文件夹中的“tese.txt”文件,称为文件的相对路径,依次类推。

此外,我们还可以直接通过文件的绝对路径

 

 fopen("D:\\MyC\\nans-friends-c-language\\Project.88\\test.txt","w");

注意这里都要是双斜杠。 


 除此之外呢,我们还需要介绍一下输入,输出这两个概念:

当我们写文件,并将文件里的信息存储在硬盘上,这样称为输出。

反之,当我们调用硬盘上已有的文件信息,将它打开呈现,称为输入。


3.文件读写

那么我们学习完如何打开和关闭文件之后,就要紧接着来学习如何对文件进行读写啦。

首先要提醒大家一点,对于文件读写一定要注意“w”和“r”的切换

文件的读写方式有两种:

顺序读写

随机读写

(1)顺序读写

文件的顺序读写有以下表格中的这些函数:

 这些函数一共分为四类,下面我们就来逐一讲解。

1)字符输入输出函数

字符输入函数        fgetc

字符输出函数        fputc

字符输入输出函数,是针对单个字符的函数,一次只能操作一个字符

 

先来看输出函数,它有两个参数:

character        要输出的字符

stream            文件操作指针 

 下面来看具体应用:

	//操作文件char ch;for(ch = 'a'; ch <= 'z'; ch++){fputc(ch, pf);}

因为每次只能操作一个字符,所以我们用for循环将26个英文字母输出给文件,来看结果:

这样我们就算是程序退出,文件里的数据依然存在,真正的实现了数据的保存

当然,如果你想要实现换行,空格这样的操作,用fputc函数也是能够实现的。 

学习完怎么向文件输出之后,我们再接着来学怎么从文件输入。


这个函数只需要一个参数,也就是文件操作指针

起初文件操作指针会指向字符的第一位每读取一次之后,指针就会往后移一位

当这个函数读取结束时,会返回EOF,所以我们也可以凭借这个来实现循环遍历。

既然是从外部输入数据,就需要东西来接收

	//操作文件int ch = 0;while ((ch = fgetc(pf))!= EOF){printf("%c ", ch);}

只要ch不是EOF,就一直遍历,得到结果如下:


2)文本行输入输出函数

顾名思义,这个函数会一次性的操作一行的数据

fputs        文本行输出函数

fgets        文本行输入函数

先来看输出函数:

第一个参数是一个指针指向我们所要操作的字符串的首地址,第二个依然是文件操作指针

	//操作文件char arr[] = "hello";fputs(arr, pf);fputs("world\n", pf);fputs("!", pf);

来看,第一个参数既然是字符串的首地址,那么我们自然就可以用数组

值得注意的是,fputs函数也不会自动换行,必须手动换行


 当这个函数读取结束时,会返回一个NULL。

 文本行输入函数相比于输出函数,多了一个num,也就是要输入的字符串大小

但是实际上我们输入的字符串大小会比num少一个

	//操作文件char arr1[100] = "0";char arr2[100] = "0";fgets(arr1, 100, pf);fgets(arr2, 3, pf);printf("%s", arr1);printf("%s", arr2);

来看,用数组来接收字符串,我们的文本文件内容为:

得到结果如下:

能够看出,不管num有多大,都只会输出一行的字符,第二个结果也应证了实际输出的字符串大小是num - 1个。 


那么我们前边说的这些个函数都是只能单一的操作字符,那么接下来,我们就来讲解一下能够操作结构体这种含有多种数据类型的函数。

3)格式化输入输出函数

先来看输出函数,第一个参数为文件操作指针后边还可以有很多个参数,没有限制

 那么到底该如何使用呢???

#include<stdio.h>
struct S
{char c;int n;float f;
};
int main()
{//打开文件FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//操作文件struct S s = { 'a', 10, 3.14 };fprintf(pf, "%c %d %f", s.c, s.n, s.f);//关闭文件fclose(pf);pf = NULL;return 0;
}

我们随便定义一个结构体类型,并输入一些数据。

大家有没有发现,实际上我们fprintf函数参数传递和我们熟知的printf函数基本一模一样,只是多了一个pf文件操作指针

不只是输出函数,输入函数亦是如此:

跟scanf函数相比,fscanf函数同样是多了一个文件操作指针pf

#include<stdio.h>
struct S
{char c;int n;float f;
};
int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//操作文件struct S s = {0};fscanf(pf, "%c %d %f", &(s.c), &(s.n), &(s.f));printf("%c %d %f", s.c, s.n, s.f);//关闭文件fclose(pf);pf = NULL;return 0;
}

得到结果: 


4) 二进制输入输出函数

所谓二进制输入输出,也就是将一组数据以二进制的形式在文件中进行读和写,此时我们的文件打开方式就要改为“rb”和“wb”啦。

我们依然是先来看输出函数,这个函数的参数就多了,分别是数据的首地址单个数据的字节大小数据的个数,以及字符操作指针

#include<stdio.h>
int main()
{//打开文件FILE* pf = fopen("test.txt", "wb");if (pf == NULL){perror("fopen");return 1;}//操作文件int arr[] = { 1,2,3,4,5 };fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);//关闭文件fclose(pf);pf = NULL;return 0;
}

将arr数组的五个整型数据以二进制的形式输出到文件,结果如下:

因为是二进制,所以是这样的结果,那到底有没有成功呢???

我们通过二进制输入函数来判断

 

 能看出来二进制输入和输出两个函数的参数完全相同

#include<stdio.h>
int main()
{//打开文件FILE* pf = fopen("test.txt", "rb");if (pf == NULL){perror("fopen");return 1;}//操作文件int arr[5] = {0};fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);for (int i = 0; i < 5; i++){printf("%d ", arr[i]);}//关闭文件fclose(pf);pf = NULL;return 0;
}

通过arr数组来接收二进制函数的输入值,并通过循环打印,得到结果如下:

可见我们的操作都是正确的。

至此,我们讲完了表中的所有文件操作函数。

但是小伙伴们有没有发现,我们讲的这些函数好像都只能从头开始按顺序操作

那么接下来,我们就接着来学习不按顺序的文件操作函数


(2)随机读写

关于文件的随机读写有以下三个函数需要我们掌握:

fseek

ftell

rewind

1)fseek

那么这个函数的作用是什么呢???我们根据它的参数来分析:

stream        我们已经很熟悉,是文件操作指针

offset        这个参数的类型是整型,它的英文含义是偏移量

origin        参数类型也是整型,英文含义为起始地

也就是说,这个函数可以帮助我们通过文件操作指针将文件内的操作系统光标移动到我们想要的起始地处也可以从起始地的左右来回偏移。        

那么对于起始地,我们只有一下三种规定的实际参数:

依次为文件的首地址文件指针当前指向的地址尾地址

 我们当前的文件内容为:

下面我们就来实际操作一下:

	//操作文件char ch = 0;fseek(pf, 4, SEEK_SET);ch = fgetc(pf);printf("%c", ch);

 来看,我们选择的起始地为文件的首地址偏移量为正则为右,为负则为左,所以此时指针从a开始向右移动四位,指向e,在通过fgetc函数进行输出,结果如下:

 现在我们的文件操作指针是指向了e,那么我们希望从e开始,去得到最后的g怎么做呢???

char ch = 0;
fseek(pf, 4, SEEK_SET);
fseek(pf, 2, SEEK_CUR);
ch = fgetc(pf);
printf("%c", ch);

很简单,只需要在指针当前位置,在向右移动两位就可以啦:

 SEEK_END同理我们这里就不在过多讲解啦。


2)ftell

这个函数就比较简单啦,它的作用是返回文件操作指针的当前位置相对于起始位置的偏移量

	//操作文件char ch = 0;fseek(pf, 4, SEEK_SET);fseek(pf, 2, SEEK_CUR);int set = ftell(pf);printf("%d", set);

我们利用上边的函数,此时我们的pf指向了‘g’,那么相对于‘a’的偏移量就是6


 3)rewind

这个函数同样简单,作用是将文件操作指针还原到文件的起始位置。

	//操作文件char ch = 0;fseek(pf, 4, SEEK_SET);rewind(pf);ch = fgetc(pf);printf("%c", ch);

得到结果如下:


四.结语

关于文件操作的相关知识到这里就讲完啦。

国庆假期后的补课生活着实艰难,这篇文章我断断续续写了三天。

最后希望各位小伙伴们能够得到自己想要的知识。

最后不要忘记一键三连哦!!!

我们下期再见!

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

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

相关文章

【重拾C语言】十二、C语言程序开发(穷举与试探——八皇后问题)

目录 前言 十二、C语言程序开发 12.1~3 自顶向下、逐步求精&#xff1b;结构化程序设计原则&#xff1b;程序风格 12.4 八皇后——穷举与试探 12.4.1 穷举法 示例&#xff1a;寻找一个整数的平方根 12.4.2 试探法 示例&#xff1a;计算给定数字的阶乘 12.4.3 穷举与试…

【【萌新的SOC学习之自定义IP核 AXI4接口】】

萌新的SOC学习之自定义IP核 AXI4接口 自定义IP核-AXI4接口 AXI接口时序 对于一个读数据信号 AXI突发读 不要忘记 最后还有拉高RLAST 表示信号的中止 实验任务 &#xff1a; 通过自定义一个AXI4接口的IP核 &#xff0c;通过AXI_HP接口对PS端 DDR3 进行读写测试 。 S_AXI…

Notepad++使用技巧

显示远程连接的文件目录 自动完成&#xff1a;函数自动提示 自动输入&#xff1a;输入一半括号自动补全另一半 自动关联 .pc文件识别为C 列模式 按住Alt不松手&#xff0c;可以直接范围选择&#xff0c;便于编辑选择的区域 关键行筛选 1.进入搜索页面的标记 2.选中标…

电商数据API接口:新服务下电商网站、跨境电商独立站,移动APP的新型拉新武器

互联网的发展改变了我们的生活方式&#xff0c;也改变了企业商家们的营销方式&#xff0c;越来越多的企业商家把产品营销从线下转到线上&#xff0c;选择在线商城、移动APP、微信公众号等互联网工具进行营销活动。而随着营销模式的多元化和电子支付渠道的进一步发展&#xff0c…

vue3前端开发系列 - electron开发桌面程序(2023-10月最新版)

文章目录 1. 说明2. 创建项目3. 创建文件夹electron3.1 编写脚本electron.js3.2 编写脚本proload.js 4. 修改package.json4.1 删除type4.2 修改scripts4.3 完整的配置如下 5. 修改App.vue6. 修改vite.config.ts7. 启动8. 打包安装9. 项目公开地址 1. 说明 本次安装使用的环境版…

Linux寄存器+Linux2.6内核进程调度队列+命令行参数+环境变量

目录 一、寄存器 二、Linux2.6内核进程调度队列 &#xff08;一&#xff09;优先级 &#xff08;二&#xff09;活动队列 &#xff08;三&#xff09;过期队列 &#xff08;四&#xff09;active指针和expired指针 三、命令行参数 &#xff08;一&#xff09;举例一 &…

燃气管网监测系统,让城市生命线更安全

万宾科技燃气管网监测系统&#xff0c;让城市生命线更安全 城市是现代社会的中心&#xff0c;拥有庞大的人口和各种基础设施&#xff0c;以满足人们的生活需求。城市基础设施包括供热&#xff0c;供水&#xff0c;管廊&#xff0c;河湖&#xff0c;建筑&#xff0c;排水&#x…

华为云云耀云服务器L实例评测|华为云耀云服务器L实例docker部署及应用(七)

八、华为云耀云服务器L实例docker、docker-compose安装及部署MySQL、Redis应用&#xff1a; 随着云原生、容器化、微服务、K8S等技术的发展&#xff0c;容器 docker 也逐渐在企业团队实践中大量的使用。它可以提供了一套标准化的解决方案&#xff0c;极大地提升了部署、发布、运…

如何在STM32中实现TCP通信?

如何在STM32中实现TCP通信&#xff1f; TCP通信在计算机网络中扮演着重要角色&#xff0c;实现它需要兼顾硬件和软件因素。 硬件层面&#xff0c;某些STM32处理器内置了Ethernet MAC&#xff0c;这有利于简化网络通信的部署。若处理器缺乏内置MAC&#xff0c;需外接以太网控制…

手把手教你用Python绘制神经网络图

接下来教大家如何使用 Python 中的 networkx 库&#xff0c;绘制美观且标准的神经网络。会根据指定的层和节点数量&#xff0c;绘制不同结构的神经网络。 networkx 库可以用来创建和操作图类型的数据结构&#xff0c;其中包括无向图、有向图、带权图等等。 神经网络可以看做是一…

柔性数组(C语言)

文章目录 1. 柔性数组的定义2. 柔性数组的特点3. 柔性数组的使用4. 柔性数组的好处 也许你从来没有听说过 柔性数组这个概念&#xff0c;但是它确实是存在的。柔性数组是C语言中一种特殊的结构&#xff0c;它允许在结构体的末尾定义一个可变长度的数组。 1. 柔性数组的定义 柔…

数学建模——平稳时间序列分析方法

目录 1、平稳性的Daniel检验 &#xff08;1&#xff09;Spearman相关系数假设检验 &#xff08;2&#xff09;时间序列平稳性的Danniel假设检验 案例 【模型分析】 1、原始数据at的平稳性检验 2、一阶差分序列的平稳性检验 3、二阶差分序列的平稳性检验 4、建立AR&#…

ChatGPT生产力|实用指令(prompt)

GPT已经成为一个不可或缺的科研生产力了&#xff0c;但是大多数人只知晓采用直接提问、持续追问以及细节展开的方式来查阅相关资料&#xff0c;本文侧重于探讨“限定场景限定角色限定主题”、“可持续追问细节展开”等多种方式来获取更多信息&#xff0c;帮人们解决更多问题。 …

二叉树的层序遍历

利用队列的先进先出&#xff0c;把根的节点的指针存到队列中&#xff0c;然后再出队列&#xff0c;在出队列时再把他的左右子树的节点指针带进去&#xff0c;循环到队列为空&#xff08;树也就遍历完了&#xff09; void LevelOrder(BTNode* root)//层序遍历 {Queue L;//定义…

Docker Compose命令讲解+文件编写

docker compose的用处是对 Docker 容器集群的快速编排。&#xff08;源码&#xff09; 一个 Dockerfile 可以定义一个单独的应用容器。但我们经常碰到需要多个容器相互配合来完成某项任务的情况&#xff08;如实现一个 Web 项目&#xff0c;需要服务器、数据库、redis等&#…

Unity角色或摄像机移动和旋转的控制脚本

该脚本挂载到需要被移动、旋转控制的物体身上&#xff0c;也可以之间挂在到摄像机上&#xff01; 挂载到摄像机上可以实现第一人称视角控制&#xff01; 挂载到物体身上&#xff0c;配合摄像机跟踪脚本可以实现&#xff0c;第三人称视角控制&#xff01; 第一人称视角 将角…

【微服务】微服务初步认识 - 微服务技术如何学习 · 认识微服务架构

微服务&#xff08;1&#xff09; 文章目录 【微服务】&#xff08;1&#xff09;1. 微服务相关技术栈2. 微服务学习路线3. 认识微服务架构3.1 单体架构3.2 分布式架构3.3 微服务(架构)3.4 微服务(架构)治理落实相关的SpringCloud、SpringCloudAlibaba和阿里巴巴的Dubbo提供的服…

【MySql】6- 实践篇(四)

文章目录 1. 为何SQL语句逻辑相同&#xff0c;性能却差异巨大1.1 性能差异大的SQL语句问题1.1.1 案例一:条件字段函数操作1.1.2 案例二:隐式类型转换1.1.3 案例三:隐式字符编码转换 2. 为何只查询一行的SQL执行很慢2.1 场景一:查询长时间不返回2.1.1 等MDL锁2.1.2 等 flush2.1.…

TCP/IP(八)TCP的连接管理(五)四次握手

一 tcp连接断开 每一个TCP报文的超时重传都由一个特定的内核参数来控制 ① 四次握手的过程 遗留&#xff1a; 谁先发送FIN包,一定是client吗? --> upload和download补充&#xff1a; 主动和被动断开连接的场景 "四次握手过程描述" F --> FIN --> F…

车载电子电器架构 —— 国产基础软件现在与未来

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…