C语言的文件函数

此篇文章主要对C语言中的" 文件读写函数 "进行详细的刨析~通过此篇文章能够了解并学习到:" 字符读写函数 "," 文本行读写函数 "," 格式化读写函数 "," 二进制读写函数 "," 文件随机读取函数 "等相关知识~

(上一篇:文件基础知识传送门:C语言的文件基础知识-CSDN博客)

文件使用方式表:

文件使用方式含义如指定文件不存在
"  r "(只读)为了输入数据,打开一个已经存在的文本文件出错
" w "(只写)为了输出数据,打开一个文本文件建立一个新文件
" a "(追加)像文本文件尾添加数据建立一个新文件
" rb "(只读)为了输入数据,打开一个二进制文件出错
" wb "(只写)为了输出数据,打开一个二进制文件建立一个新文件
" ab "(追加)向一个二进制文件尾添加数据出错
" r+ "(读写)为了读和写,打开一个文本文件出错
" w+ "(读写)为了读和写,建立一个新的文件建立一个新文件
" a+ "(读写)打开一个文件,在文件尾进行读写建立一个新文件
" rb+ "(读写)为了读和写,打开一个二进制文件出错
" wb+ "(读写)为了读和写,新建一个新的二进制文件建立一个新文件
" ab+ "(读写)打开一个二进制文件,在文件尾进行读和写建立一个新文件

一、文件的打开与关闭

① 文件的打开函数

在我们编写程序,想要实现对文件的读写前,我们需要先打开一个文件,我们可以看到fopen的参数:filename 是文件名,其代表的就是想要进行操作的对应文件(有时文件并不在程序中,我们可以将此处替换成此文件的绝对路径)。

mode 代表的是文件的打开模式,文件的打开模式:

(文件打开成功时,返回文件起始位置的文件指针;文件打开失败时,返回空指针NULL!!!)

测试代码

int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen");return 1;}return 0;
}

注意!!!!!!

在我们使用fopen去打开一个文件时,为了安全,我们要判断接收文件指针的变量pf是否为空(也就是判断文件是否成功被打开~),如果打开失败,则输出错误原因,并退出此次运行。

这段代码...是不是少了些什么呢?(注意参考动态内存管理中的内存开辟...此代码只"打开"了,但没有"关闭"~)

知道了!既然我们打开了一个文件,相应的就需要关闭文件~

② 文件的关闭函数

此函数的作用就是用于关闭文件。

FILE* stream 指的是想要关闭的文件。

(关闭文件后,需要将pf置空,否则pf会变成危险的野指针!!!)

(就像之前学习动态内存管理时,使用free释放内存后,需要再置空一次~)

那么既然了解了文件的打开与关闭,让我们将两者融合,练习一下~:

int main()
{FILE* pf = fopen("data1.txt", "w");//检查是否打开失败if (pf == NULL){perror("fopen");return 1;}fclose(pf);//防止pf变成野指针pf = NULL;
}

注意在我们运行此代码之前,我的电脑中是没有"data1.txt"文件

而当我们运行代码后再进行查看,会发现文件中出现了"data1.txt"

这是因为 " w " 在查找不到目标文件时,就会创建一个新文件,并且命名为目标文件的名字。

而当我们将此文件删除,再使用 " r " 来读取该文件会发生什么呢:

没错,就是报错(找不到该文件)

(如果我们将文件 "data1.txt" 存入其他的文件路径中,那么对 "data1.txt" 的读取是否能够成功呢?)

我们可以看到,这种情况下是无法找到该文件的,那么这时我们就不能找到这个文件了吗?答案是,可以找到~因为此时我们输入的文件名并不完整,所以它找不到另一个文件路径中的该文件,此时我们可以将全部文件名写入:"data1.txt"—>"D:\\data1.txt"

(正常是一个\,写\\会防止\与后面字符结合)

此时我们可以看到,再次进行文件的查找,就能够找到了~

二、文件的顺序读写函数

顺序读写函数表:

功能函数名适用于
字符输入函数fgetc

所有输入流

字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwrite文件

① 字符输出函数 fputc

我们可以看到,fputc函数有两个参数

int character:代表想要写入文件的字符。

FILE* stream:代表操作的目标文件。

fputc函数的作用:将character代表的字符写入文件stream中,并将位置标识符向前移动。

📚代码演示:创建一个 data.txt ,然后使用fputc函数分别写入 "hello" 到文件中。

int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen");return 1;}int arr[5] = { 'h','e','l','l','o' };for (int i = 0; i < 5; i++){fputc(arr[i], pf);//或者直接fputc('h',pf);...}                     //的依次存入也可以fclose(pf);           //因为每次存入,位置标识符自动向前移动.pf = NULL;return 0;
}

代码运行前:

代码运行后:

② 字符输出函数 fgetc

可以看到,fgetc函数的参数只有FILE* stream

fgetc函数的作用:从指定的stream中提取下一个字符,并且位置标识符自动向前移动。

(如果调用流时流在文件末尾,则函数返回EOF并设置流的文件末尾标识符)

(如果发生读取错误,函数返回EOF并设置流的错误标识符(ferror))

📚代码演示:将"data.txt"中的数据进行读取并输出:

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

代码运行前:(该文件中已经存入了"hello")

代码运行:

③ ​​​​​​​文本行输出函数 fputs

我们可以看到,fputs函数的参数有两个:

const char* str:存储想要传递的数据(字符串)。

FILE* stream:用于接收数据的目标文件。

fputs函数的作用:将字符串str中的数据传输到stream中。

📚代码演示:使用 fputs 函数向文件"data.txt"中写入"hello world","abcdef"。

int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen");return 1;}char str0[20] = "hello world\n";char str1[20] = "abcdef";fputs(str0, pf);//也可以直接fputs("hello world\n",pf);fputs(str1, pf);fclose(pf);pf = NULL;return 0;
}

代码运行后:

(注意:fputs函数是可以接收换行符 '\n' 的)

④ ​​​​​​​文本行输出函数 fgets

fgets函数的参数有三个:

char* str:代表接收文件数据的字符串。

int num:代表传输的字符个数。

FILE* stream:代表提取数据的文件。

fgets函数的作用:从流中获取字符串,并将其作为C字符串存入str中,直到 (num - 1) 字符读取完毕,或到达新行文件的末尾。

📚代码演示:使用 fgets 函数读取上题中data.txt中的两行字符串:

int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}char str[100];fgets(str, 13, pf);printf("%s", str);fgets(str, 7, pf);printf("%s", str);fclose(pf);pf = NULL;return 0;
}

运行代码:

(需要注意的是: "hello world\n" 中共有12个字符,而输出12个字符,要将num设置为13~)

⑤​​​​​​​ 格式化输出函数 fprintf

fprintf函数的参数有三个:

FILE* stream:想要写入的目标文件。

const char* format:格式化数据的对应类型。

"......":格式化数据的名字。

fprintf函数的作用:将格式化的数据写入文件。

或许这样看有些不大明了,没关系,让我们对比一下与它极其极其极其相似的函数~:printf

怎么样,这样看来思路就清晰了许多吧?没错,printf与fprintf的差别就是:

"printf是标准输出流的输出函数,用于将输出输出到屏幕上;而fprintf则是向文件输出,二者均是输出函数,只是目标不同~"

📚代码演示:创建一个学生结构体,写入"姓名","年龄","成绩",并初始化数据然后使用 fprintf 将数据存入文件 "data.txt" 中。

struct stu
{char name[20];int age;double score;
};
int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen");return 1;}stu s = { "xiaowang",20,99.5 };fprintf(pf, "%s %d %lf", s.name, s.age, s.score);fclose(pf);pf = NULL;return 0;
}

代码运行:

⑥ ​​​​​​​格式化输出函数 fscanf

fscanf函数的参数同样有三个:

FILE* stream:代表从该文件中读取数据。

const char* format:数据的类型。

"...":对应数据的名字。

fscanf函数的作用:对文件中格式化的数据进行读取。

同样的,让我们与"scanf"进行一下比较:

fscanf 与 scanf 二者区别:

"scanf是从标准输入(键盘)读取格式化数据;而fscanf是从文件中读取格式化的数据(所有的输入流),二者读取数据的位置不同~"

📚代码演示:将上段代码中存入文件"data.txt"中的学生信息读取到一个结构体中,并将其数据打印到屏幕上。

struct stu
{char name[20];int age;double score;
};
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}stu s = {};fscanf(pf, "%s %d %lf", s.name, &s.age, &s.score);printf("%s %d %lf", s.name, s.age, s.score);fclose(pf);pf = NULL;return 0;
}

运行代码:

(注意:字符数组名字就是地址,故不用取地址)

⑦ ​​​​​​​二进制输出函数 fwrite

fwrite函数有四个参数!!!:

const void* ptr:代表要写入的元素数组。

size_t size:要写入的每个元素的大小(以字节为单位)。

size_t count:代表元素数目。

FILE* stream:指定的目标文件。

fwrite函数的作用:从ptr指向的内存块写入计数元素数组。

(每个数组的大小为数据类型大小的字节。)

(流的位置指示符由写入的字节总数来提高。)

📚代码演示:定义一些数据,并使用 fwrite 输入到文件"data.txt"中:

int main()
{FILE* pf = fopen("data.txt", "wb");if (pf == NULL){perror("fopen");return 1;}int num[5] = { 1,2,4,8,16 };int sz = sizeof(num[0]);int len = sizeof(num) / sizeof(num[0]);fwrite(num, sz, len, pf);fclose(pf);pf = NULL;return 0;
}

运行代码:

欸?此时我们会发现,存入文件中的似乎都是乱码,我们打开文件夹中的文件再查看一下:

仍然是乱码,这是怎么回事呢?

其实这是因为,fwrite是二进制形式的写入,而在文档中一般采用的都是ASCII存储方式,所以当我们直接将二进制形式写入文件中,自然就是看不懂的~

而其实我们强大的VS编译器能够解决这种问题:

而我们也可以使用下面的函数做到读取~

⑧ ​​​​​​​二进制输入函数 fread

此函数的参数与上一个函数的参数是一样的(除了ptr不再被const保护)~

fread的作用:从流stream中读取数据到 ptr 所指的数组中。

📚代码演示:

int main()
{FILE* pf = fopen("data.txt", "rb");if (pf == NULL){perror("fopen");return 1;}int num[5] = { 0 };int sz = sizeof(num[0]);fread(num, sz, 5, pf);int len = sizeof(num) / sizeof(num[0]);for (int i = 0; i < len; i++){printf("%d ", num[i]);}fclose(pf);pf = NULL;return 0;
}

运行代码:

三、文件的随机读写函数

① ​​​​​​​文件指针定位函数 fseek

fseek函数有三个参数:

FILE* stream:进行操作的文件。

long int offset:相对 origin 的偏移量。

int origin:表示开始添加偏移 offset 的位置,它一般指定为以下列常量之一:

常量描述
SEEK_SET文件的开头
SEEK_CUR文件指针的当前位置
SEEK_END文件的末尾

fseek函数的作用:可以根据文件指针所指向的位置,以及偏移量来指定数据。

📚代码演示:在文件"data.txt"中随便写一些数据,并读取(此时文件中有"abc")

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

那如果我想要单独得到一个b,改如何去得到呢?

此时我们就可以使用fseek函数:

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

② ​​​​​​​返回偏移量函数 ftell

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

📚代码演示:使用ftell返回偏移量。(此时文件中为"abcdef")

#include <stdio.h>int main(void) 
{FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}fseek(pf, 3, SEEK_CUR); int ch = fgetc(pf);printf("%c\n", ch);int ret = ftell(pf);printf("%d\n", ret);fclose(pf);pf = NULL;return 0;
}

③ ​​​​​​​文件指针返回起始位置函数 rewind

功能于其名称一样,就是使给定的文件开头,让文件返回起始位置。

📚代码演示:将上一段代码的文件使用rewind返回起始位置。

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

代码运行:

好了,那么关于文件的读写函数相关知识就为大家讲解到这里啦~如果有哪里写的不够清楚或者不够详细的,还请各位多多指出,我也会吸取教训多多改正的,那么我们下一期再见啦~

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

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

相关文章

深度学习与应用:人体关键点检测

实验二 深度学习与应用&#xff1a;人体关键点检测 1、 实验目的 了解人体关键点检测基础流程熟悉YOLOV7-pose模型结构掌握 YOLOv7-pose 模型的训练、Fine-tuning 以及推理的能力掌握YOLOV7-pose模型对实际问题的应用能力&#xff0c;了解如何在特定的场景和任务中应用该模型…

【机器学习】——线性回归(自我监督学习)

文章目录 1. 线性回归的定义2. 线性回归的模型3. 线性回归的核心思想4. 线性回归的求解5. 线性回归的假设6. 模型评估7. 线性回归的优缺点8. 线性回归的扩展9. 线性回归的实际应用10. 示例代码&#xff08;Python实现&#xff09; 线性回归详细介绍 1. 线性回归的定义 线性回归…

53 语言模型(和之后用来训练语言模型的数据集)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录理论部分使用计数来建模N元语法总结 代码读取长序列数据随机采样顺序分区 小结练习 理论部分 在上一部分中&#xff0c;我们了解了如何将文本数据映射为词元&#xff0c;以及将这些词元可以视为一系列离散的观测&#xff0c;例如单词或字符…

.bixi勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复

导言 在当今数字化时代&#xff0c;勒索软件已成为企业和个人面临的重大安全威胁。.bixi勒索病毒作为其中一种新型恶意软件&#xff0c;以其快速加密文件的能力和高效传播机制引发了广泛关注。该病毒不仅能够迅速锁定用户的关键数据&#xff0c;还常常在感染后施加极大的心理压…

邀请功能的实现分析

邀请功能 功能分析 场景&#xff1a;项目中出现用户邀请其他用户加入群组的功能 需求&#xff1a;用户点击生成邀请链接可以生成一个url&#xff0c;将这个url分享给其他用户&#xff0c;其他用户点击后对用户登录状态进行校验&#xff0c;校验通过即可加入群组&#xff0c;未…

江协科技STM32学习- P16 实验-TIM输出比较(PWD驱动LED呼吸灯,舵机,直流电机)

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

如何访问字符串中某个字符

在Java中&#xff0c;你可以使用字符串的charAt(int index)方法来查看字符串中第index个位置的字符。index是从0开始的&#xff0c;也就是说&#xff0c;字符串的第一个字符的索引是0&#xff0c;第二个字符的索引是1&#xff0c;以此类推。如果索引超出了字符串的长度&#xf…

IO多路转接:select、poll、epoll

目录 非阻塞读取 fcntl函数 I/O多路转接之select select函数 fd_set结构 select的模拟实现 select的优缺点 I/O多路转接之poll poll函数 struct pollfd结构体 poll函数的使用示例 poll的模拟实现 poll的优缺点 I/O多路转接之epoll epoll的三个系统调用 epoll的…

当你在Linux系统中使用MySQL命令行工具查询数据库时,如果中文显示为问号(?)或其他乱码,简单解决办法。(2)

文章目录 1、问题出现2、解决办法 1、问题出现 2、解决办法 mysql -u [username] -p --default-character-setutf8 [database_name]rootab66508d9441:/# mysql -uroot -p123456 --default-character-setutf8 tingshu_album mysql: [Warning] Using a password on the command …

Redis 字符串类型的典型应用场景

目录 1. 缓存功能 2. 计数功能 3. 共享会话&#xff08;Session&#xff09; 4. 手机验证码 前言 这里将详细介绍 Redis 字符串类型在实际开发中的几个典型应用场景&#xff0c;并提供相应的伪代码示例。 1. 缓存功能 场景描述 在许多Web应用中&#xff0c;数据通常需要…

使用AVL树实现Map

一、数组在裂变扩容时可能会出现环、在数组元素转为链表之后选择尾插法插入节点、数组到链表到AVL到RBT的转换 1、数组在裂变扩容时链表中的节点计算出来的位置可能也会发生变化&#xff0c;在多线程情况下调整节点位置可能会出现环。 2、数组中的数组元素转为链表后插入新节点…

在大模型训练中,为什么GPU 通常比 CPU 更重要

在大模型训练中&#xff0c;GPU 通常比 CPU 更重要&#xff0c;原因主要有以下几点&#xff1a; 一、并行计算能力 GPU 拥有强大的并行计算能力。在大模型训练中&#xff0c;需要处理海量的数据和复杂的计算任务。例如&#xff0c;深度学习模型中的矩阵运算、卷积运算等&…

13. 了解人工智能可能存在的偏见

这篇文章没有太多技术和代码细节&#xff0c;更多的是作为一份有趣的报告。 这里没有任何模型会被训练。 这篇文章也为生成式人工智能导论课程中 HW8: Safety Issues of Generative AI 提供中文引导。 代码文件下载 文章目录 为什么人工智能存在偏见&#xff1f;动手试试加载模…

算法_BFS解决多源最短路问题---持续更新

文章目录 前言引入矩阵题目要求题目解析代码如下 飞地的数量题目要求题目解析代码如下 地图中的最高点题目要求题目解析代码如下 地图分析题目要求题目解析代码如下 前言 本文将会向你介绍有关宽度优先搜索&#xff08;BFS&#xff09;解决多源最短路问题的相关题型&#xff1…

故障诊断│GWO-DBN灰狼算法优化深度置信网络故障诊断

1.引言 随着人工智能技术的快速发展&#xff0c;深度学习已经成为解决复杂问题的热门方法之一。深度置信网络&#xff08;DBN&#xff09;作为深度学习中应用比较广泛的一种算法&#xff0c;被广泛应用于分类和回归预测等问题中。然而&#xff0c;DBN的训练过程通常需要大量的…

机器人速度雅可比矩阵(机器人动力学)

博途PLC矩阵求逆 矩阵求逆 博图SCL_博图矩阵运算-CSDN博客文章浏览阅读839次。本文介绍如何用C语言实现矩阵求逆的过程,详细解析了相关代码,适合线性代数和编程爱好者学习。https://rxxw-control.blog.csdn.net/article/details/122367883 1、二自由度平面关节机器人速度雅…

项目第十二弹:功能联调

项目第十二弹&#xff1a;功能联调 一、发布订阅功能测试1.生产者2.消费者3.演示4.持久化信息查看1.消息2.SQLite3数据库 二、持久化恢复测试1.代码2.gc3.演示 三、虚拟机和信道隔离测试1.责任划分2.如何测试3.生产者4.消费者5.演示 一、发布订阅功能测试 我们直接上TOPIC交换…

MySQL中的逻辑条件

逻辑条件组合两个比较条件的结果来产生一个基于这些条件的单个的结果&#xff0c;或者逆转一个单个条件的结果。当所有条件的结果为真时&#xff0c;返回行。 SQL的三个逻辑运算符是&#xff1a; AND、OR、NOT 可以在WHERE子句中用AND和OR运算符使用多个条件。 示例一&#…

惊爆!高通要收购英特尔,巨头也会被时代抛弃!

今天看到的外媒消息&#xff0c;高通要收购英特尔&#xff0c;看到消息的时候&#xff0c;其实&#xff0c;还是挺吃惊的。 高通是移动芯片的王者&#xff0c;英特尔是 PC 芯片的王者。当然了&#xff0c;英特尔这个可能需要再加上两个字&#xff1a;曾经的 PC 芯片王者。 其实…

植物大战僵尸【源代码分享+核心思路讲解】

植物大战僵尸已经正式完结&#xff0c;今天和大家分享一下&#xff0c;话不多说&#xff0c;直接上链接&#xff01;&#xff01;&#xff01;&#xff08;如果大家在运行这个游戏遇到了问题或者bug&#xff0c;那么请私我谢谢&#xff09; 大家写的时候可以参考一下我的代码思…