【C语言】深度探讨文件操作(一)

请添加图片描述

文章目录

  • 📝前言
  • 🌠 为什么使用文件?
    • 🌉什么是文件?
  • 🌠程序文件
    • 🌉数据文件
  • 🌠文件名
    • 🌉二进制文件和文本文件?
  • 🌠文件的打开和关闭
    • 🌉 流和标准流
  • 🌠标准流
    • 🌉 文件指针
  • 🌠文件的打开和关闭
    • 🌉 ⽂件的顺序读写
  • 🌠文件拷贝
  • 🚩总结


📝前言

本小节,我们学习文件操作的知识,为什么使用文件?什么是文件?程序文件和数据文件,文件名的构成,二进制文件和文本文件?
文件的打开和关闭,认识 流和标准流,利用 ⽂件的顺序读写,最后进行了简单文件拷贝,干货满满!学习起来吧😃!

🌠 为什么使用文件?

文件提供了一种简单而有效的持久数据存储和交换机制,这是使用文件最主要的原因:存储持久数据。文件可以用于持久地存储数据,即使程序终止或计算机重新启动,文件中的数据也会保留。这提供了一种数据存储的方式。

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次运行程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。

🌉什么是文件?

文件是计算机系统中用来存储和组织数据的基本单位。
磁盘上的文件是文件。
但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)

🌠程序文件

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

🌉数据文件

文件的内容不一定是程序,而程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。本小节讨论的是数据文件在以前各章所处理数据文件的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行到显示器上。其实有时候我们会把信息输出到磁盘上,当需要的时候再从数据读取到内存中使用,这里处理的就是磁盘上的文件。

🌠文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件主干+文件后缀
注:文件名主干不能包含以下非法字符: \ / : * ? " < > |
例如:D:\CSDN\test.txt

文件路径:D:\CSDN\
文件主干:test
文件后缀:.txt

如图:
在这里插入图片描述

🌉二进制文件和文本文件?

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
在这里插入图片描述

数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件,则需要在存储前转换。以ASCll字符的形式存储的文件就是文本文件

那一个数据在内存中是怎么存储的呢?
字符一律以ASCll形式存储,数值型数据可以用ASCll形式存储,也可以使用二进制形式存储。

如有整数10000,如果以ASCll码的形式输出到磁盘,则磁盘中占用5个字节(每个字符个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2019测试)。
在这里插入图片描述

当你看到这里,你是不是有点晕,阿森也是,不过不慌,让我们测试一下代码,以下代码不理解没关系,文章后面会慢慢讲解

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
	int a = 10000;
	FILE* pf = fopen("test.txt", "wb");
	//使用fopen函数打开一个名为test.txt的文件,以二进制写模式("wb")打开。如果文件不存在则创建。
	fwrite(&a, 4, 1, pf);//二进制文件写到文件中
	fwrite函数将a变量的内容写入到文件中。
	&a 是要写入数据的指针
	4  表示每个数据单元的大小,这里a是int型,大小为4字节
	1  表示要写入的数据单元个数,这里只写入一个a变量
	pf 是FILE指针,指向已打开的文件
	fclose(pf);//调用fclose关闭之前打开的文件。
	pf = NULL;
	return 0;
}

test.txt直接在文件中打开图:
在这里插入图片描述
VS上用二进制编辑器打开test.txt在这里插入图片描述

10000的二进制表示:00 00 27 10
在内存中按小端存储:10 27 00 00

在这里插入图片描述

🌠文件的打开和关闭

🌉 流和标准流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据数据,不同外部设备的输入输出的操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出流的概念,我们可以把流想象成流淌着字符的河。
这么抽象,不太好理解,让我们看图:
当我们要给程序输入数据可以有很多方式:键盘输入/文件读取/网络传输…输出方式可以是打印屏幕/写到文件中…这些写进程序里的方式肯定不同,有所差异,如果把所有的输入输出的方式的操作都学习,会感到有些繁琐,因此引进流的概念。
在这里插入图片描述
C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

🌠标准流

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有流呢?
那是因为C语言程序在启动的时候,默认打开了3个流:

  • stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。
  • stdout - 标准输出流,大多数的环境中输出至显示器界面,printf函数就是讲信息输出到标准流中
  • stderr - 标准错误流,大多数的环境中输出到显示器界面。
    这是默认打开了这三个流,我们使用scanf ,printf等函数就可以直接进行输入输出操作的。
    stdin,stdout,stderr三个流的类型是FILE*,通常称为文件指针
    C语言中,就是通过FILE*的文件指针;来维护流的各种操作的。

🌉 文件指针

缓冲文件系统中,关键概念是:“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.

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

不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同⼩异。
每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信息,使⽤者不必关⼼细节。
⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便。
下⾯我们可以创建⼀个FILE*的指针变量:

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

定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件
在这里插入图片描述

🌠文件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。
在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了指针和⽂件的关系。
ANSIC 规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。
fopen是C语言中用于打开文件的函数。
原型:

FILE *fopen(const char *filename, const char *mode);
filename是要打开的文件的的路径和名称
mode是打开文件的模式:只读、只写、追加等等

fopen函数返回一个指向FILE类型的指针,该指针可以用于后续的文件操作,比如读取、写入和关闭文件。

例如,要以只读方式打开名为"example.txt"的文件,可以这样使用fopen函数:

FILE *pf = fopen("example.txt", "r");

以绝对路径打开:

FILE *pf = fopen("C:\\Users\\Asen\\Desktop", "w");
加多一个\可以防止\与后面的内容结合发生转义
相对路径
. 表示当前目录
.. 表示上一级路径
FILE* pf = fopen("./../../data.txt", "w");

fclose是C语言中用于关闭文件的函数。
原型:

int fclose(FILE *stream);
stream是指向FILE类型的指针,即要关闭的文件的指针
fclose函数用于关闭先前由fopen、freopen或tmpfile打开的文件。

注:关闭文件后,将释放与该文件相关的所有缓冲区,并将文件指针置为NULL

例如:要关闭先前打开的文件

FILE *file = fopen("example.txt", "r");
// 对文件进行读操作
fclose(pf);
pf=NULL;

fclose函数返回一个整数值,如果成功关闭文件,则返回0,如果出错,则返回EOF


总体结合例子:

int main()
{
	//打开文件,为了写
	//如果文件打开失败,会返回NULL
	FILE* pf = fopen("data.txt", "w");
	if (NULL == pf)
	{
		perror("fopen");
		return 1;
	}
	//写文件

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

代码写在哪个文件目录下,默认自动在代码文件目录底下自动添加data.txt文件,效果图:
在这里插入图片描述
总结mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:

⽂件使⽤⽅式含义如果指定文件不存在
“r”(只读)为了输⼊数据,打开⼀个已经存在的⽂本⽂件出错
“w”(只写)为了输出数据,打开⼀个⽂本⽂件建⽴⼀个新的⽂件
“a”(追加)向⽂本⽂件尾添加数据建⽴⼀个新的⽂件
“rb”(只读)为了输⼊数据,打开⼀个⼆进制⽂件出错
“wb”(只写)为了输出数据,打开⼀个⼆进制⽂件建⽴⼀个新的⽂件
“ab”(追加)向⼀个⼆进制⽂件尾添加数据建⽴⼀个新的⽂件
r+”(读写)为了读和写,打开⼀个⽂本⽂件出错
“w+”(读写)为了读和写,建议⼀个新的⽂件建⽴⼀个新的⽂件
“a+”(读写)打开⼀个⽂件,在⽂件尾进⾏读写建⽴⼀个新的⽂件
“rb+”(读写)为了读和写打开⼀个⼆进制⽂件出错
“wb+”(读写)为了读和写,新建⼀个新的⼆进制⽂件建⽴⼀个新的⽂件
“ab+”(读写)打开⼀个⼆进制⽂件,在⽂件尾进⾏读和写建⽴⼀个新的⽂件
#include <stdio.h>
int main()
{
	FILE* pFile;
	//打开⽂件
	pFile = fopen("myfile.txt", "w");
	//⽂件操作
	if (pFile != NULL)
	{
		fputs("fopen example", pFile);
		//fputs是C语言中用于向文件写入字符串的函数
		//关闭⽂件
		fclose(pFile);
	}
	return 0;
}

运行效果图:
在这里插入图片描述

🌉 ⽂件的顺序读写

文件的顺序读写是指按照文件中数据的顺序逐个读取或写入数据。

顺序读写函数介绍:

函数名功能适⽤于
fgetc字符输⼊函数所有输⼊流
fputc字符输出函数所有输出流
fgets⽂本⾏输⼊函数所有输⼊流
fputs⽂本⾏输出函数所有输出流
fscanf格式化输⼊函数所有输⼊流
fprintf格式化输出函数所有输出流
fread⼆进制输⼊⽂件
fwrite⼆进制输出⽂件

注:表格中的每个函数名可以点击,可以为你进行更深入的查找
上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀般指适⽤于标准输出流和其他输出流(如⽂件输出流)。
以下是程序进行简单读写操作流程图:
在这里插入图片描述
看到这里,让我们在文件进行写内容吧!

fputc是C语言中用于向文件写入单个字符的函数
它的原型是:

int fputc(int c, FILE *stream);
c是要写入的字符
stream是指向FILE类型的指针,即要写入的文件的指针。
fputc函数将字符c写入到指定的文件流中

fputc函数返回一个非负值(通常是写入的字符),如果成功写入字符,则返回非负值,如果出错,则返回EOF

让我们直接看代码:

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	fputc('d', pf);

	//打印26个字符a~z
	//int i = 0;
	//for (i = 0; i < 26; i++)
	//{
	//	fputc('a'+i, pf);
	//	//fputc('\n', pf);
	//}
	
	//输出到终端
	//int i = 0;
	//for (i = 0; i < 26; i++)
	//{
	//	fputc('a' + i, stdout);
	//	//fputc('\n', pf);
	//}

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

效果图:

终端无内容:
在这里插入图片描述
文件上写进去abcd了
在这里插入图片描述
标准输出流stdout输出到终端
在这里插入图片描述

fgetc是C语言中用于从文件中读取单个字符的函数
它的原型是:

int fgetc(FILE *stream);
stream是指向FILE类型的指针,即要读取的文件的指针。
fgetc函数从指定的文件流中读取一个字符并返回其ASCII码值。

需要注意的是,fgetc函数返回的是int类型的值,因为它不仅可以返回字符的ASCII码值,还可以返回EOF(通常为-1)作为读取失败的标识。
上代码看看:

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	int ch = fgetc(pf);//a
	printf("%c\n", ch);

	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;
}

在这里插入图片描述

为什么可以这样写呢?
在这里插入图片描述

🌠文件拷贝

写一个代码,完成将data1.txt文件的内容,拷贝一份生成data2.txt文件
思路:从data1.txt中读取数据,写到data2.txt的文件中

#include <stdio.h>

int main()
{
    // 打开要读取的文件
    FILE* pfread = fopen("data1.txt", "r");
    if (pfread == NULL)
    {
        perror("fopen->data1.txt"); // 如果打开失败,输出错误信息
        return 1;
    }
    // 打开要写入的文件
    FILE* pfwrite = fopen("data2.txt", "w");
    if (pfwrite == NULL)
    {
        fclose(pfread); // 关闭已打开的文件流
        pfread = NULL; // 将指针置为NULL,避免误用
        perror("fopen->data2.txt"); // 如果打开失败,输出错误信息
        return 1;
    }
    // 数据的读写(拷贝)
    int ch = 0;
    while ((ch = fgetc(pfread)) != EOF)
    {
        fputc(ch, pfwrite); // 逐个读取字符并写入到另一个文件
    }

    // 关闭文件流
    fclose(pfread);
    fclose(pfwrite);

    return 0;
}

data1.txt内容要不我把文章内容都放进去吧,然后都复制到data2.txt

在这里插入图片描述

data2.txt效果图:

在这里插入图片描述


🚩总结

这次阿森和你一起学习为什么使用文件?什么是文件?程序文件和数据文件,文件名的构成,二进制文件和文本文件?文件的打开和关闭, 流和标准流,文件指针,文件的打开和关闭,⽂件的顺序读写,对前面的知识利用实现简单文件拷贝。当然还scanf/fscanf/sscanfprintf/fprintf/sprintf的对比,ftell计算返回⽂件指针相对于起始位置的偏移量等等阿森正在快马加鞭中。

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器人电机综述 — 电机分类、舵机、步进与伺服、物理性质和伺服控制系统

电机综述 图片与部分素材来自知乎大佬不看后悔&#xff01;最全的电机分类&#xff0c;看这一篇就够了&#xff01; - 知乎 (zhihu.com)&#xff0c;本文只是把机器人中常用的电机知识提炼了一下 1 按照结构和工作原理划分 1. 同步电机 ​ 电机的转速与定子磁场的转速相同步…

《WebKit 技术内幕》之八(1):硬件加速机制

《WebKit 技术内幕》之八&#xff08;1&#xff09;&#xff1a;硬件加速机制 1 硬件加速基础 1.1 概念 这里说的硬件加速技术是指使用GPU的硬件能力来帮助渲染网页&#xff0c;因为GPU的作用主要是用来绘制3D图形并且性能特别好&#xff0c;这是它的专长所在&#xff0c;它…

k8s 使用cert-manager证书管理自签

个人建议使用安装更快&#xff0c;比helm快&#xff0c;还要等待安装crd kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml#官网 https://cert-manager.io/docs/installation/kubectl/#创建自签的ClusterIssuer c…

数据库设计最佳实践:学院个人信息管理系统中的MySQL优化

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【C++记忆站】类和对象(一)

类和对象(一) 1.面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题 C是基于面向对象的&#xff0c;关注的是对象&#xff0c;将一件事情拆分成不同的对象&#xff0c;靠对象之间…

2024年热门项目管理软件推荐:提升项目管理效率的工具集合

项目管理系统软件有哪些&#xff1f;本文将根据项目管理系统软件的功能、选择项目管理系统软件对公司的好处&#xff0c;根据国际上知名软件评测网站G2 Grid的评测结果对8款2024年好用的项目管理软件&#xff1a;Zoho Projects、Smartsheet、monday、Asana、ClickUp、Notion、A…

elasticsearch备份恢复,elasticdump使用

准备环境 1. 将node-v10.23.1-linux-x64.tar.xz上传到服务器/usr/local目录下 2. tar xf node-v10.23.1-linux-x64.tar.xz 3. 将node_modules.tar.gz上传到服务器/usr/local目录 4. tar -zxvf node_modules.tar.gz 5. 设置NODE环境 5.1 vim /etc/profile export NODEJS_…

YOLOv5全网首发:新一代高效可形变卷积DCNv4如何做二次创新?高效结合SPPF

💡💡💡本文独家改进:DCNv4更快收敛、更高速度、更高性能,与YOLOv5 SPPF高效结合 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/category_12511931.html 💡💡💡全网独家首发创新(原创),适合paper !!! 💡💡💡 2024年计算机视觉顶会创…

[python]使用pyqt5搭建yolov8钢筋计数一次性钢材计数系统

【官方框架地址】 github地址&#xff1a;https://github.com/ultralytics/ultralytics 【算法介绍】 Yolov8是一种先进的深度学习模型&#xff0c;用于目标检测和识别。在钢筋计数任务中&#xff0c;Yolov8可以有效地识别和计数图像中的钢筋。下面是对如何使用Yolov8实现钢筋…

Java SE入门及基础(25)

目录 方法带参&#xff08;续第24篇&#xff09; 6.方法参数传递规则 方法传参来自官方的说明 基本数据类型传值案例 基本数据类型传值时传递的是值的拷贝 引用数据类型传值案例 引用数据类型传值时传递的是对象在堆内存上的空间地址 Java SE文章参考:Java SE入门及基础知…

【C++第二课 - 类和对象上 - 入门知识】struct类、class类、访问限定符、this指针

目录 面向过程与面向对象初步认识类的定义struct定义类class定义类 类的访问限定符及封装访问限定符 声明与定义分离this指针类成员的命名问题this 类的实例化类的对象大小的计算成员函数为何不在对象里面类对象大小计算 面向过程与面向对象初步认识 C语言是面向过程的&#x…

线程和进程的区别(从JVM角度出发)

进程与线程的区别 线程具有许多传统进程所具有的特征&#xff0c;故又称为轻型进程(Light—Weight Process)或进程元&#xff1b;而把传统的进程称为重型进程(Heavy—Weight Process)&#xff0c;它相当于只有一个线程的任务。在引入了线程的操作系统中&#xff0c;通常一个进…

Linux 的提示符太长了,帮你精简一下

普通用户修改文件 ~/.bashrc 修改 50 行左右的代码&#xff0c;将两个w改为大写的W 如果是root用户则修改文件/root/.bashrc&#xff0c;同样的方法。

自然语言推断:注意力之注意(Attending)

注意&#xff08;Attending&#xff09; 第一步是将一个文本序列中的词元与另一个序列中的每个词元对齐。假设前提是“我确实需要睡眠”&#xff0c;假设是“我累了”。由于语义上的相似性&#xff0c;我们不妨将假设中的“我”与前提中的“我”对齐&#xff0c;将假设中的“累…

数据结构——循环链表

1.循环单链表 最后一个结点的指针不是NULL,而是指向头结点 单链表和循环单链表的比较&#xff1a; 单链表&#xff1a;从一个结点出发只能找到该结点后续的各个结点&#xff1b;对链表的操作大多都在头部或者尾部&#xff1b;设立 头指针&#xff0c;从头结点找到尾部的时间…

《Windows核心编程》若干知识点应用实战分享

目录 1、进程的虚拟内存分区与小于0x10000的小地址内存区 1.1、进程的虚拟内存分区 1.2、小于0x10000的小地址内存区 2、保存线程上下文的CONTEXT结构体 3、从汇编代码角度去理解多线程运行过程的典型实例 4、调用TerminateThread强制结束线程会导致线程中的资源没有释放…

代码随想录 Leetcode1047. 删除字符串中的所有相邻重复项

题目&#xff1a; 代码(首刷自解 2024年1月21日&#xff09;&#xff1a; class Solution { public:string removeDuplicates(string s) {if (s.size() < 2) return s;stack<char> t;for (int i 0; i < s.size(); i) {if (t.empty()) t.push(s[i]);else {if (s[i…

shell编程学习

shell编程学习 变量的高级用法变量替换字符串处理获取字符索引获取子串长度抽取字符串中的子串 案例测试 变量的高级用法 变量替换 ##变量替换&#xff08;贪婪&#xff0c;从前往后匹配&#xff0c;匹配到进行删除&#xff09; test1I love you,you love me echo $test1 han…

人工智能攻克奥数几何难题:AlphaGeometry 接近金牌选手水平

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

PLC物联网网关BL104实现PLC协议转MQTT、OPC UA、Modbus TCP

随着物联网技术的迅猛发展&#xff0c;人们深刻认识到在智能化生产和生活中&#xff0c;实时、可靠、安全的数据传输至关重要。在此背景下&#xff0c;高性能的物联网数据传输解决方案——协议转换网关应运而生&#xff0c;广泛应用于工业自动化和数字化工厂应用环境中。 无缝衔…