长文介绍文件操作各类函数,读写各类数据操作,再也不用担心程序运行结束后输入的数据丢失

文件操作

💛💛笔记自取~ :文件操作笔记💛💛
❤️欢迎喜欢学习C/C++的朋友互关一起努力!!❤️



导言

前几天发布了有关通讯录的文章,在输入我的联系人信息后,如果退出了程序,我的联系人信息就会消失。我们想要的通讯录可能不是这样,我们希望的是通讯录可以一直保存着我们的联系人信息

通讯录文章指路:简易通讯录实现,gitee上还更新了动态内存版本的通讯录

本文将介绍,通过文件操作,保存我们的联系人信息

一、常见文件类型

文件的分类如下图

在这里插入图片描述


1. 程序文件

  • 🌟源程序文件(后缀为.c)

  • 🌟目标文件(windows环境后缀为.obj)

  • 🌟可执行程序(windows环境后缀为.exe)

2. 数据文件

✨有时我们运行程序时候需要读取数据,进行内容的输出

3. 文件名

✨相信文件名大家都很熟悉是如何构成的

文件路径+文件名主干+文件后缀

  • 例如: c:\code\test.txt

二、文件指针

❗️❗️操作文件步骤:

  • ⭐️打开文件
  • ⭐️读取/写入数据
  • ⭐️关闭文件

🌟我们每次打开文件时,都会有个指针指向一个文件信息区,用来存放文件的相关信息(如文件名、文件状态及文件当前的位置等)

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

实际上在vs2019中,corecrt_wstdio.h头文件中就有描述

在这里插入图片描述

vs2019可能封装的很深,不太好观察

我们来看看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结构的变量,这样使用起来更加方便

在这里插入图片描述

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


三、文件的打开和关闭

1. fopen打开文件

//打开文件
FILE * fopen (const char * filename, const char * mode);

❗️❗️如果打开失败,返回空指针❗️❗️

  • filename是文件名
  • mode是打开方式

✨✨mode打开方式有什么样的类型呢

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

本文主要是介绍和了解前三个方式


2. "r"读取文件

先来试试读取文件

我先在桌面新建一个txt文件

在这里插入图片描述

按照打开文件的代码书写,注意防止空指针

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE * f = fopen("file.txt", "r");
    if (f == NULL)
    {
        perror("fopen");//判断打开成功
        return -1;
    }

    return 0;
}

❗️❗️结果是它告诉我没有这个文件!

❗️❗️❗️注意:这种只写名称的写法叫传递相对路径地址,如果只写文件名称,该文件必须在源文件同一路径下才能读取成功

在这里插入图片描述

🌟我们需要书写它的全部路径,这种写法叫传递绝对路径地址

右击这个文件,找到属性,找到它的路径

在这里插入图片描述

🌟修改代码:注意这里\和字母连起来会造成转义字符,所以需要在前面添加\为了反转义

int main()
{
    FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "r");
    if (f == NULL)
    {
        perror("fopen");
        return -1;
    }


    return 0;
}

💢💢💢运行之后,我仍然读取失败??

在这里插入图片描述

找了半天才知道原因在哪

在这里插入图片描述

🔥🔥🔥原来我的电脑在新建文件的时候,不用自己给它定义是什么类型文件,它会自动添加,而我写的file.txt才是文件名,txt才是文件类型,所以在查找它路径的时候顺便看一下名称是否正确

这次我们不修改代码了,直接修改文件名

在这里插入图片描述

🔥这次读取成功了!

我们在文件里输入file这几个字符,试试如何把读取的字符打印出来

fgetc 读取字符函数

参数和返回值

int fgetc( FILE *stream );
int main()
{
    FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "r");
    if (f == NULL)
    {
        perror("fopen");
        return -1;
    }
    int ret = fgetc(f);
    printf("%c", ret);
    
    ret = fgetc(f);
    printf("%c", ret);
    
    return 0;
}

运行结果,如我们所愿

在这里插入图片描述


3. fclose关闭文件

//关闭文件
int fclose ( FILE * stream );//传的是文件信息区的地址

直接利用上面打开文件的代码,在后面添加关闭文件

✨✨✨注意用完后指针指向的地方没有变,需要把它变成空指针✨✨✨

int main()
{
    FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "r");
    if (f == NULL)
    {
        perror("fopen");
        return -1;
    }

    fclose(f);
    f = NULL;

    return 0;
}

4. "w"写入文件

💗我们把刚才桌面建的file文件删掉,再执行原来的代码,但打开方式是"w"

int main()
{
    FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "w");
    if (f == NULL)
    {
        perror("fopen");
        return -1;
    }

    fclose(f);
    f = NULL;

    return 0;
}

结果是

✨桌面上多了刚刚新增的文件,如果在文件中写东西,再运行代码,文件又会变成空文件,很神奇
在这里插入图片描述


我们还能在代码里面给file文件添加字符

fputc字符输入函数

参数和返回值

int fputc( int c, FILE *stream );

把文件地址传进去,给文件写入字符

int main()
{
    FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "w");
    if (f == NULL)
    {
        perror("fopen");
        return -1;
    }
    fputc('f', f);//字符输入函数
    fputc('i', f);
    fputc('l', f);
    fputc('e', f);

    fclose(f);
    f = NULL;

    return 0;
}

运行后我们打开file文件

在这里插入图片描述

我们用代码给文件中写入了file


四、文件的顺序读写

顺序读写函数表

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数(读取包含’\0’)fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数(按照一种格式读取)fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出(按二进制写入)fwrite文件

✨外部设备:文件、屏幕、网络、光盘、软盘

✨而流是指:可以读写到某个设备

c语言程序运行起来就默认打开3个流

标准输出流 - stdout(屏幕打印,这里屏幕就是编译器调试控制台)

💫标准输入流 - stdin(键盘)

💫标准错误流 - stderr

💫这几个流类型都是FILE*


前面已经介绍了字符输入函数fgetc字符输出函数fputc

1. fputs 文本行输出函数

参数与返回值

int fputs( const char *string, FILE *stream );

传参传,写入的文本行和文件的地址

成功返回0

首先桌面上没有这个文件,我们需要写一个文件,写入字符串内容为file

#include <stdio.h>
#include <stdlib.h>

int main()
{
	FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "w");
	if (p == NULL)
	{
		perror("fopen");
		return -1;
	}
	fputs("file", p);

	fclose(p);
	p = NULL;

	return 0;
}

运行结构是,桌面上多出个文件,test.txt,内容为file,比之前一个一个字符输入要简单多

在这个代码运行完的基础上,我们读取一下这个字符串吧


2. fgets 文本行输出函数

参数与返回值

char *fgets( char *string, int n, FILE *stream );

传参:存放字符串的位置,传多少字节(最多不会超过最字节数),文件地址

ps:这里传的字节中默认包含'\0'

成功返回string的起始地址

直接上代码看吧:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "r");
	if (p == NULL)
	{
		perror("fopen");
		return -1;
	}
	char arr[5] = { 0 };
	char* ret = fgets(arr, 5, p);

	printf("%s\n", arr);
	printf("%s\n", ret);

	fclose(p);
	p = NULL;

	return 0;
}

运行结果:

在这里插入图片描述

两种打印方式均可


3. fprintf 格式化输出函数

参数和返回值

int fprintf( FILE *stream, 
            const char *format [, argument ]...);

传参:文件地址,格式化内存(此格式化,为该内容有一定的格式)

类似于printf的""中的功能

返回值:成功返回写入几个字节

代码与前几个类似

struct stu
{
	int a;
	char b[4];
}st = {233, "abc"};

int main()
{
	FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "w");
	if (p == NULL)
	{
		perror("fopen");
		return -1;
	}
	int ret = fprintf(p, "%d %s", st.a, st.b);

	printf("%d", ret);
	fclose(p);
	p = NULL;

	return 0;
}

效果:

在这里插入图片描述

在这里插入图片描述


4. fscanf 格式化输入函数

参数和返回值

int fscanf( FILE *stream, const char *format [, argument ]... );

传参:文件地址,格式化内存(此格式化,为该内容有一定的格式)

根据上面已经输出的代码,我们来输入进结构体中

#include <stdio.h>
#include <stdlib.h>

struct stu
{
	int a;
	char b[4];
}st;

int main()
{
	FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "r");
	if (p == NULL)
	{
		perror("fopen");
		return -1;
	}

	fscanf(p, "%d %s", &(st.a), st.b);
	printf("%d %s\n", st.a, st.b);

	fclose(p);
	p = NULL;

	return 0;
}

运行结果:

在这里插入图片描述


5. fwrite 二进制输出函数

参数和返回值

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

参数:指针指向要被写入的数据,一个元素的字节大小,最大的元素个数,文件地址

返回值:真正被书写入的元素个数

实验一下:

struct stu
{
	int a;
	char b[4];
}st = {233, "abc"};


int main()
{
	FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "wb");
	if (p == NULL)
	{
		perror("fopen");
		return -1;
	}

	int ret = fwrite(&st, sizeof(st), 1, p);

	printf("%d", ret);
	fclose(p);
	p = NULL;

	return 0;
}

运行结果:

在这里插入图片描述

这里字母按照二进制形式写进去就是字母,而二进制写入数字我们看不到


6. fread 二进制输入函数

参数与返回值

size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

参数:指针指向读取的数据放置的位置,一个元素的字节大小,最大的元素个数,文件地址

也是按照上面代码读取

struct stu
{
	int a;
	char b[4];
}st;



int main()
{
	FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "rb");
	if (p == NULL)
	{
		perror("fopen");
		return -1;
	}
	fread(&st, sizeof(st), 1, p);

	printf("%d %s", st.a, st.b);
	
	fclose(p);
	p = NULL;

	return 0;
}

在这里插入图片描述

看来读取成功了


小结

看到这已经疑惑很久了,🌟fprintf和printf到底有什么区别,是不是有什么联系呢

scanf/fscanf/sscanf

printf/fprintf/sprintf

这两组非常相似的函数到底是什么

函数作用
scanf从标准输入流(键盘)读取格式化的数据
fscanf从所有的输入流读取格式化的数据
sscanf从字符串中读取一个格式化的数据(把一个字符串转换成一个格式化的数据)
printf把格式化的数据输出到标准输出(屏幕)上
fprintf把格式化的数据输出到所有输出流(屏幕/文件)上
sprintf把格式化的数据转换成对应的字符串!

虽然我们日常不会用程序去写一个txt文件,但我们需要知道这种操作原理,

🌟在必要的时候,也会带来便利!

我有百度搜索过

在这里插入图片描述

其实它的例子就隐藏在我们生活中😆😆😆~~


  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凛音Rinne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值