文件的操作

news2025/2/21 21:57:05

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

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

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


一.什么是文件

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

 

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

  • 程序文件
  • 数据文件

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.coloradmin.cn/o/1088425.html

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

相关文章

国内ITSM发展的趋势

多年来&#xff0c;随着客户业务需求、工作文化、技术创新的不断变化以及新的IT环境的出现&#xff0c; IT支持也出现了新的变化&#xff0c;由单一的IT帮助台&#xff08; IT help desk&#xff09;逐渐转变为了综合性的IT服务台&#xff08; IT service desk&#xff09;&…

基于intel D4系列深度相机 (D455)的单点测距和两点距离测量(代码讲解超详细)

目录 1.简介 1.1 intel D4系列深度相机 (D455) 1.2结构光(Structured-light) 2.基于深度相机的单点测距和两点距离测量 2.1函数详解 2.2 全部代码 1.简介 1.1 intel D4系列深度相机 (D455) Intel RealSense D435、D455等D4系列&#xff1a; Intel D4系列深度相机是由英特…

有人因它丢掉offer!背景调查到底有什么魔力?

求职时&#xff0c;公司往往会对应聘者过往经历做一些背景调查——也被叫做背调。 据i背调人才研究院数据显示&#xff0c;2022年的求职失信占比仍然较高&#xff0c;有39.13%的候选人存在职业信用异常。而随着近些年来&#xff0c;整体就业市场和职业发展中竞争压力不断加剧&…

间歇性微服务问题...

在Kubernetes环境中&#xff0c;最近由于特定配置导致Pod调度失败。哪种 Kubernetes 资源类型&#xff08;通常与节点约束相关&#xff09;可能导致此故障&#xff0c;尤其是在未正确定义的情况下&#xff1f; 节点选择器资源配额优先级污点Pod 中断预算 已有 201 人回答了该…

【学习之路】Multi Agent Reinforcement Learning框架与代码

【学习之路】Multi Agent Reiforcement Learning框架与代码 Introduction 国庆期间&#xff0c;有个客户找我写个代码&#xff0c;是强化学习相关的&#xff0c;但我没学过&#xff0c;心里那是一个慌&#xff0c;不过好在经过详细的调研以及自身的实力&#xff0c;最后还是解…

A星寻路优化方案

1.存储常用位置间的路径信息&#xff08;此优化方案一般用于地图较大&#xff0c;且起始点离终点较远情况下&#xff09; 对于地图信息已知且各障碍位置固定的情况下&#xff0c;可以将常用的位置及其之间的路径信息保存&#xff0c;然后寻路时分别找到离开始点&#xff0c;结…

【JUC】原子操作类及LongAddr源码分析

文章目录 1. 十八罗汉2. 原子类再分类2.1 基本类型原子类2.2 数组类型原子类2.3 引用类型原子类2.4 对象的属性修改原子类2.5 原子操作增强类 3. 代码演示及性能比较&#xff1a;4. LongAddr原理5. LongAddr源码分析5.1 add()5.2 longAccumulate()5.3 sum() 6. 小总结6.1 Atomi…

【JUC】CAS原理

CAS原理 文章目录 CAS原理1. 概述2. 原理3. Unsafe4. CAS与自旋锁 1. 概述 compare and swap的缩写&#xff0c;中文翻译成比较并交换,实现并发算法时常用到的一种技术。它包含三个操作数一一内存位置、预期原值及更新值 执行CAS操作的时候&#xff0c;将内存位置的值与预期原…

和鲸 × 北中医:高规格、高并发,一场真正的人工智能分析应用临场实践考核

百舸争流&#xff0c;奋楫者先&#xff0c;2023 年&#xff0c;人工智能已逐渐成为引发千行百业产生颠覆性变革的前沿技术。随着人工智能时代的加速到来&#xff0c;我国政产学研各界对于创新型、复合型 AI 人才的能力要求正不断提升。 面对一批又一批激流勇进、乘风破浪的莘莘…

【Leetcode】 738. 单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 示例 1: 输入: n 10 输出: 9 示例 2: 输入: n 1234 输出: 1234 示例3 : …

精通Maven的捷径:一文包揽所有必知必学

Maven是每个Java程序都会遇到的包管理工具&#xff0c;今天整理一下Maven的相关知识&#xff0c;从青铜到王者&#xff0c;一文全了解&#xff0c;我们开始吧&#xff01; 1、maven是什么&#xff0c;为什么存在&#xff1f;项目结构是什么样子&#xff0c;怎么定位jar 官方网…

【ROS】ros-noetic和anaconda联合使用【教程】

【ROS】ros-noetic和anaconda联合使用【教程】 文章目录 【ROS】ros-noetic和anaconda联合使用【教程】1. 安装anaconda2. 创建虚拟环境3. 查看python解释器路径4. 在虚拟环境中使用任意的包5. 创建工作空间和ros功能包进行测试Reference 1. 安装anaconda 在Ubuntu20.04中安装…

统一观测丨使用 Prometheus 监控 SQL Server 最佳实践

作者&#xff1a;啃唯 SQL Server 简介 SQL Server 是什么&#xff1f; Microsoft SQL Server 是 Microsoft 推出的关系型数据库解决方案&#xff0c;支持企业 IT 环境中的各种事务处理、商业智能和分析应用程序。Microsoft SQL Server 是市场领先的数据库技术之一。 SQL S…

【算法-动态规划】最长上升子序列-力扣 300

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

无限连接:前端跨页面通信的实现与应用

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 引言 1. 前端跨页面通信的概述 2. 前端跨页…

RunnerGo测试平台,无代码玩转UI自动化测试

首先需要进入官网&#xff0c;RunnerGo支持开源&#xff0c;可以自行下载安装&#xff0c;也可以点击右上角体验企业版按钮快速体验 点击体验企业版进入工作台后可以点击页面上方的UI自动化 进入到测试页面 创建元素 我们可以在元素管理中创建我们测试时需要的元素 这里我们以…

PPT制作软件哪个好?各种类型的10款好用PPT软件盘点!

想必大家对PPT并不陌生&#xff0c;它在日常的工作或学习中有诸多应用场景&#xff0c;小到制作一份电子相册&#xff0c;大到一场产品发布会&#xff0c;都可以看到PPT的影子。 如果我们细究PPT的话&#xff0c;会发现可以细分出各种类型的PPT软件&#xff0c;诸如&#xff1…

Linux指令集合

磁盘相关命令 df//查看磁盘整体状况df -h //查看磁盘整体状况du //查看磁盘使用情况&#xff0c;如果不加目录&#xff0c;默认为当前目录du -h -d 0//表示我们只希望看当前目录磁盘使用情况&#xff0c;不会继续展示这层目录的下一级目录 du -h -d 1//表示展示当前目录的下一…

微信自动批量添加好友的方法

在现在的营销中微信已成为一种重要的沟通方式。微信目前是没有自动批量添加好友的功能&#xff0c;需要运营者一个一个手动去添加&#xff0c;这样太过于浪费时间&#xff0c;并且加频繁了还容易被封号&#xff0c;今天给大家介绍几种手动批量加好友的方式以及怎么借助第三方软…

深入理解AQS之ReentrantLock源码分析

开题&#xff1a;如何自己生成一把独占锁&#xff1f; 1. 管程 — Java同步的设计思想 管程&#xff1a;指的是管理共享变量以及对共享变量的操作过程&#xff0c;让他们支持并发。 互斥&#xff1a;同一时刻只允许一个线程访问共享资源&#xff1b; 同步&#xff1a;线程之间…