【C语言】小王带您实现文件操作(简单图示讲解)

news2024/11/24 13:04:45

说到文件操作,大家会第一印象想到不就是电脑硬盘中创建文件,写入数据吗,键盘、鼠标就可以搞定,那么接下来我要告诉你的是C语言也可以实现文件操作哦!!!

目录

前言

一、为什么要使用文件操作

二、文件

2.1 程序文件

2.2数据文件

2.3 文件名

三、文件操作

3.1 文件打开和关闭

1. 文件指针

2. 文件的打开和关闭

3.2 文件的顺序读写

3.3 函数使用

1. fgetc和fputc

2. fgets和fputs

3. fscanf和fprintf

4. fread和fwrite

5. 对比一组函数

3.4 文件的随机读写

1. fseek

2.ftell

3.rewind

3.5 文本文件和二进制文件

3.6文件读取结束的判定

总结


前言

首先、我们要实现的文件操作,不只是简单的打开和关闭,写入数据,我们也可以从相对应文件中,读取数据,到程序中,使得程序能顺利运行,并对传来的数据进行处理使用!


一、为什么要使用文件操作

我们在前文中,实现了静态、动态通讯录,但是我们也发现了,只要程序关闭,那么添加的通讯录的信息数据,也就一起消失,我们下次打开的时候,通讯录是空的,那么这不太符合我们实际所需。我们需要的是,可以利用、可以存储、可以保存的通讯录,那么我们就必须了解一下,文件操作。

文件操作:使用文件操作我们可以将数据直接存放到电脑的硬盘上,做到数据的持久化

二、文件

磁盘上的文件就是文件。

在程序设计中,我们一般说的文件有两种,分别是程序文件、数据文件(从文件功能的角度进行分类)。

2.1 程序文件

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

2.2数据文件

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

由上文对于两种文件的介绍,我们可知,我们需要了解的是数据文件,本文就主要讲解一下,如何处理数据文件,并对该文件进行操作

2.3 文件名

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

比如:D:\new.txt    这是D盘下的文件名为new的文本文档

三、文件操作

我们之前在写C语言程序的时候,都是通过键盘和屏幕,进行输入和输出数据,我们这一次要将数据输入和读取数据都是于硬盘有关。

3.1 文件打开和关闭

我们首先要了解的是我们之前一直所处理的,输入输出数据都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。

如图:

文件操作需要的是,以硬盘中的文件为数据输入输出对象。

如图:
 

1. 文件指针

在缓冲文件系统中,关键的概念是 “ 文件类型指针 ”,即文件指针

文件信息区:

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名 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结构的变量,并填充信息,一般都是通过一个FILE指针来维护这个FILE结构的变量,方便使用

如图:

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

2. 文件的打开和关闭

文件在使用前应该先打开,之后再关闭,我们先认识一下C语言给我们提供的文件开关函数

第一个fopen来打开文件,第二个fclose来关闭文件

代码如下:

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

打开方式:

1.r表示读         w表示写        a表示追加(即不会覆盖原有数据)

2.带b,标识的是打开二进制文件

3. +号表示读写,如果是r+的话,且没有指定文件,那么会报错,但是w+、a+若没有指定文件,会创建一个指定文件,再输入数据(写文件)

代码演示:

3.2 文件的顺序读写

什么叫做顺序读写呢,意思就是,在读取/写文件的时候,读取一个或者写一个内容进去,光标就会向后移动一位,有顺序的读取和写入

如图:

 通过fgetc和fputc函数解释了,文件确实是按照顺序读写的

3.3 函数使用

输入流和输出流介绍:

流的概念:

上面函数使用于所有输入流或文件,这是什么意思?

图示解释如下:

1. fgetc和fputc

上文图示用到这两个函数

代码如下:

//这是fgetc函数,可以理解为得到文件中的一个字符
int main()
{
	FILE* pf = fopen("test.txt", "r");
	for (int i = 0; i < 10; i++) {
		char src = fgetc(pf);
		printf("%c", src);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}
//这是fputc函数,可以理解为放置文件中一个字符
int main()
{
	FILE* pf = fopen("test.txt", "w");
	for (int i = 0; i < 26; i++) {
		fputc('a' + i, pf);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

实际上,fgetc和fputc函数就是得到或放置一个字符在文件中,返回值为int

2. fgets和fputs

fgets和fputs,可以理解为得到或者放置一个字符串在文件中。

还有一个特点,如图所示:

 fgets如果没有读取到内容,返回NULL

代码如下:

//fgets函数
int main()
{
	FILE* pf = fopen("test.txt", "r");
	char arr[20];
	while (fgets(arr, 20, pf) != NULL) {
		printf("%s", arr);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}
//fputs函数
int main()
{
	FILE* pf = fopen("test.txt", "w");
	char arr[20];
	fputs("hello world!!\n", pf);
	fputs("hello why\n", pf);
	fputs("hello fzx\n", pf);

	fclose(pf);
	pf = NULL;
	return 0;
}

3. fscanf和fprintf

实际上和scanf、printf差不多的形式,只是多一个参数 FILE*stream

如图:

 如果fscanf读取的时候没有数据,那么也不会报错,就认为是没有读取吧

代码如图所示:

struct S {
	char name[20];
	int age;
	double score;
};
//fscanf格式化读取pf流中的数据,赋值给后面的参数,和scanf用法差不多,
//只是多一个FILE*类型,后面于scanf用法一致,改用&就用
int main()
{
	FILE* pf = fopen("test.txt", "r+");
	//读写都行
	//格式化输出
	//struct S s = { "why",19,100 };
	struct S s = {0};
	struct S s1 = { 0 };
	fscanf(pf, "%s %d %lf\n", s.name, &(s.age), &(s.score));
	printf("%s %d %lf\n", s.name, (s.age), (s.score));
	//fscanf(pf, "%s %d %lf\n", s1.name, &(s1.age), &(s1.score));
	//printf("%s %d %lf\n", s1.name, (s1.age), (s1.score));
	int a = 10;
	/*fscanf(pf, "%d", a);
	printf("%d\n", a);*/  //这是进行测试如果没有相应的数据读取的时候,不会报错,只是相当于没有这一行代码,不会发生任何改变(对相应a)
	fclose(pf);
	pf = NULL;
	return 0;
}

//这是fprintf函数的使用
int main()
{
	FILE* pf = fopen("test.txt", "r+");
	//读写都行
	//格式化输出
	struct S s = { "why",19,100 };
	fprintf(pf, "%s %d %lf\n", s.name, (s.age), (s.score));
	//printf("%s %d %lf\n", s.name, (s.age), (s.score));
	fclose(pf);
	pf = NULL;
	return 0;
}

1.fscanf 格式化读取pf流中的数据,赋值给后面的参数,和 scanf 用法差不多,只是多一个FILE*类型,后面于 scanf 用法一致,该用&就用

2.fprintf 格式化写入文件中数据,上面代码中,将结构体变量s,数据,写入pf管理的文件中

3.fscanf 如果没有相应的数据读取的时候,不会报错,只是相当于没有这一行代码,不会发生任何改变(对相应a)

4. fread和fwrite

只能接收文件流,二进制文件,可能会有乱码产生

如图分析:

代码演示:

struct S {
	char name[20];
	int age;
	double score;
};
//先写入二进制文件	fwrite
int main()
{
	struct S s = { "fzx",18,100 };
	FILE* pf = fopen("test.txt", "wb");
	//写一个二进制文件
	fwrite(&s, sizeof(struct S), 1, pf);

	fclose(pf);
	pf = NULL;
	return 0;
}
//再读取二进制文件 fread
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("test.txt", "rb");
	//读取文件
	fread(&s, sizeof(struct S), 1, pf);
	printf("%s %d %lf\n", s.name, s.age, s.score);
	return 0;
}

5. 对比一组函数

scanf / fscanf / sscanf

printf / fprintf /sprintf

前面四种大家都认识了,那么sscanf和sprintf是什么呢

如图所示:

3.4 文件的随机读写

1. fseek

根据文件指针的位置和偏移量来定位文件指针。(改变光标位置)

 代码演示:

int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "wb");
	fputs("This is an apple.", pFile);
	fseek(pFile, 9, SEEK_SET);
	fputs(" sam", pFile);
	fclose(pFile);
	return 0;
}

2.ftell

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

 代码演示:

int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "wb");
	int num = ftell(pFile);
	printf("%d ", num);
	return 0;
}

3.rewind

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

图文演示:

 代码演示:

int main()
{
	FILE* pf = fopen("test.txt", "w");
	for (int i = 0; i < 10; i++) {
		fputc('a' + i, pf);
	}
	int num = ftell(pf);
	printf("%d \n", num);
	rewind(pf);
	printf("%d \n", ftell(pf));
	fclose(pf);
	pf = NULL;
	return 0;
}

3.5 文本文件和二进制文件

1.根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
2.数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
3.如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储     的文件就是文本文件。

字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

测试文本文件和二进制文件代码:

int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}

3.6文件读取结束的判定

在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束,而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

1.文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )

   fgetc 判断是否为 EOF .
   fgets 判断返回值是否为 NULL 

2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。

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

图示:

代码演示:

//文本文件
int main(void)
{
  int c; // 注意:int,非char,要求处理EOF
  FILE* fp = fopen("test.txt", "r");
  if(!fp) {
    perror("File opening failed");
    return EXIT_FAILURE;
 }
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
  while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
 {
   putchar(c);
 }//判断是什么原因结束的
  if (ferror(fp))
    puts("I/O error when reading");
  else if (feof(fp))
    puts("End of file reached successfully");
  fclose(fp);
}

总结

主要讲解文件操作的的一些函数,fopen、fclose、fgets、fwrite等等函数的介绍,以及对于文件的分类、输入和输出流的分析,以及更多文件操作的函数图示分解。

下文我们将使用文件操作,升级通讯录。

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

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

相关文章

模拟卷.C

1.分支_sine之舞 样例输入 3 样例输出 ((sin(1)+3)sin(1-sin(2))+2)sin(1-sin(2+sin(3)))+1 2.数组_和最大子序列 样例输入 5 3 -2 3 -5 4 样例输出 4 3.二维数组_星辰大海 样例输入 2 2 S. #T 2 RD DR 3 S.# .#. .T# 3 RL DDD DDRR 样例输出 I get …

文档管理系统采用电子签名的优势

DocuWare文档管理系统始终提供最高级的安全性&#xff0c;保护我们客户的机密文件和数据。 现在&#xff0c;我们与信任服务提供商ValidatedID 集成&#xff0c;电子签名又向前迈出了重要的一步。每当组织需要证明签名是真实的并确保文档未被更改时&#xff0c;都可以使用这些…

开源mybatis神器

什么是通用 Mapper&#xff1f; 它是一个可以方便的使用 Mybatis 进行单表的增删改查优秀开源产品。它使用拦截器来实现具体的执行 Sql&#xff0c;完全使用原生的 Mybatis 进行操作。在 Github 上标星 5.9K&#xff01; 为什么要用 Mapper&#xff1f; 它提供了所有单表的基…

《SQL基础》07. 约束

SQL-约束约束常见约束案例外键约束删除/更新行为约束 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确性、有效性和完整性。 分类&#xff1a; 约束描述关键字非空约束限制该字段的数据不能为n…

动态内存管理(2)

TIPS 1. scanf读取与空格&#xff1a; 我们都知道&#xff0c;scanf()在从输入缓冲区里面读取数据的时候&#xff0c;如果中间碰到了空格&#xff0c;那么就会直接停下来&#xff0c;而如果在最前面有个空格&#xff0c;直接无视空格。 2. scanf()读取与\n&#xff0c;如果是…

【论文精选】TPAMI2020 - PFENet_先验引导的特征富集网络_小样本语义分割

【论文精选】TPAMI2020 - PFENet_先验引导的特征富集网络_小样本语义分割 精选精析&#xff1a; 【论文原文】&#xff1a; Prior Guided Feature Enrichment Network for Few-Shot Segmentation (当前引用次数&#xff1a;184) 【论文代码】&#xff1a; https://github.co…

【爪洼岛冒险记】第5站:多图解,超详细讲解Java中的数组、二维数组--建议收藏

&#x1f331;博主简介&#xff1a;是瑶瑶子啦&#xff0c;一名大一计科生&#xff0c;目前在努力学习JavaSE。热爱写博客~正在努力成为一个厉害的开发程序媛&#xff01; &#x1f4dc;所属专栏&#xff1a;爪洼岛冒险记【从小白到大佬之路】 ✈往期博文回顾: 【爪洼岛冒险记】…

【GD32F427开发板试用】RT-THREAD标准版 移植使用

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;打盹的消防车 前言&#xff1a; 无意在微信看到了GD做活动&#xff0c;想到了第一时间体验一下&#xff0c;搭配RT-THREAD&#xff0c;也很方…

【Java】【系列篇】【Spring源码解析】【三】【体系】【BeanDefinition体系】

整体结构图 1. BeanDefinition 用于保存 Bean 的相关信息&#xff0c;包括属性、构造方法参数、依赖的 Bean 名称及是否单例、延迟加载等&#xff0c; 它是实例化 Bean 的原材料&#xff0c;Spring 就是根据 BeanDefinition 中的信息实例化 Bean。 2. 我们获取对象的方式一般有…

AioDnsBrute:一款功能强大的异步DNS爆破工具

关于AioDnsBrute AioDnsBrute是一款功能强大的异步DNS爆破工具&#xff0c;该工具基于Python 3.5开发&#xff0c;并使用了asyncio库以实现针对目标域名的异步爆破。 该工具的运行速度非常快&#xff0c;在一台小型VPS主机上&#xff0c;可以实现在1.5-2分钟之内处理大约10万…

【计数DP】P4933 大师

这道是洛谷官方题单的简单DP为啥我放上来呢&#xff0c;因为我因为各种各样的细节原因没做出来感觉计数的DP有点点难&#xff0c;得多写了P4933 大师 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)题意&#xff1a;思路&#xff1a;第一眼肯定是设dp[i][j]为以a[i]为结尾&…

8. 【Redisson源码】分布式信号量RSemaphore

目录 一、RSemaphore的使用 二、RSemaphore设置许可数量 三、RSemaphore的加锁流程 四、RSemaphore的解锁流程 【本篇文章基于redisson-3.17.6版本源码进行分析】 基于Redis的Redisson的分布式信号量RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。 …

从首个「数实融合」公益球场,看元宇宙奏响创新「三重奏」

作者 | 曾响铃 文 | 响铃说 2022年的元宇宙&#xff0c;一半是海水&#xff0c;一半是火焰。 一边是刮起元宇宙热潮的Roblox股价跌去大半&#xff0c;Meta也因元宇宙亏损深陷泥潭。另一边&#xff0c;经过2021年元宇宙概念落地和普及&#xff0c;2022年却也是元宇宙相关产业…

分享86个PHP源码,总有一款适合您

PHP源码 分享86个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 86个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1fsoGdkr_-wZUaJvVMOlihQ?pwdlhyo 提取码&#xff…

Java 泛型是什么?一文带你吃透泛型

文章目录1. Java 泛型2. 泛型类3. 泛型接口4. 泛型方法5. 泛型集合Java编程基础教程系列1. Java 泛型 Java 泛型是 JDK1.5 中引入的一个新特性&#xff0c;其本质是参数化类型&#xff0c;把类型作为参数传递。其主要的形式有泛型类&#xff0c;泛型接口和泛型方法。泛型概念的…

sqoop安装(linux)

一、前期准备安装好hadoop伪分布安装好MySQL下载sqoop压缩文件实验环境&#xff1a;实验环境版本CentOS 6.5MySQL5.7.37hadoop3.3.0sqoop1.4.7sqoop1.4.7 下载链接&#xff1a;https://pan.baidu.com/s/16AUdtBmSv7OG2PTyA1XcgQ?pwdqu7lmysql驱动包下载地址&#xff1a;https:…

易于设置的倒计时页面Easy countdown

今天开始放假了 什么是 Easy countdown &#xff1f; Easy countdown 是一个易于设置的倒计时页面。可以设置为倒计时或计时器。 先看看官方提供的动图 安装 在群晖上以 Docker 方式安装。 在注册表中搜索 easy-countdown &#xff0c;选择第一个 yooooomi/easy-countdown&am…

【前端学习指南】基础开发环境搭建

&#x1f36d; Hello&#xff0c;我是爱吃糖的范同学 邻近春节&#xff0c;虽然学校的事情已经处理的差不多了&#xff0c;又开始要忙着找实习......时间安排上还是有很多问题&#xff0c;希望大家多多包涵&#xff0c;我已经加班加点在写作了&#x1f602;&#x1f602;&…

高盐废水如何处理,离子交换树脂在高盐废水中的应用

什么是高盐废水&#xff1f; 高盐废水是工业废水中较常见的一种&#xff0c;它是指总含盐量(以NaCl计&#xff09;至少为1%的废水&#xff0c;属于难处理的废水之一。 高盐废水中的总溶解固体物TDS&#xff0c;多在10000-25000mg/L&#xff0c;含盐成分复杂&#xff0c;有Na、…

vue3中echarts组件的最佳封装形式

项目中经常用到echarts&#xff0c;不做封装直接拿来使用也行&#xff0c;但不可避免要写很多重复的配置代码&#xff0c;封装稍不注意又会过度封装&#xff0c;丢失了扩展性和可读性。始终没有找到一个好的实践&#xff0c;偶然看到一篇文章&#xff0c;给了灵感。找到了一个目…