C语言:通讯录的文件操作,保存你的联系人到硬盘中吧

前言:本人为C语言初学者,学识尚浅,研究程度存在很大的局限性,眼界很窄。以下所有观点仅代表个人见解和思路,各位游刃有余的前辈可以给予批评和指正!各位与鄙人同路的学子可相互探讨、发表看法,交换观点!

本文建立在:C语言:动态内存分配,来一个动态的通讯录 的基础上,请务必先掌握动态通讯录的开辟方法。

当然,我首先准备好了一个带菜单的版本,如果你真的懒得写菜单,那可以先用这个作为模板:动态内存分配通讯录。我们将在这个通讯录上操刀,完成文件操作。

目录

1.尝试在结束程序时保存数据

2.在启动程序时读取数据


文件操作 - 顾名思义,我们需要将通讯录中联系人的信息保存到电脑的硬盘中去,然后再次启动通讯录的时候能够读取他们。所以我们基本上就确定了操刀的地方:在启动程序初始化通讯录时来读取数据;在退出程序时来保存数据!

1.尝试在结束程序时保存数据

可能你好奇为什么我不先读取数据。首先,如果你第一次启动这个程序,哪来的联系人信息给你i读取?其次,我们想想读取联系人信息需要哪些步骤:判断是否是初次启动决定是否打开文件、是否可以一次性读取?显然不行,必须一个个读取来判断增容~可见读取十分复杂,不如先从保存入手,简单明了,不管咋样,保存总是那么直接的。

显然,我们需要把保存函数放在 EXIT 里,也就是用户输入 0 准备退出的时候:这个时候声明并定义一个函数,我这里起名为:WriteData;

void WriteData(struct contact* c);

保存数据首先我们要找到数据在哪儿吧!所以这里我们肯定要把通讯录这个结构体传过去,通过其中的 data 指针我们可以找到我们需要的所有数据。

首先,我们得有一个文件来存储数据,那我们得打开并获得文件指针。写数据,fwrite,如果忘记了,就来看一看:

 4个参数,我们现在有哪些?数据的指针:有,数据大小:有,数据个数:有,文件指针???自然,我们这里必须用 fopen 来搞一个文件指针出来。当然,如果你又忘了 fopen 也没关系,查就完了!

显然,第一个是文件名,第二个是打开模式。我们要写文件,肯定是用写的模式打开,而且写文件你得知道,就算没有文件,也给你创造文件,所以也省去了很多麻烦。

这里我以二进制读写为例:

void WriteData(struct contact* c)
{
	FILE* ptr = fopen("condata.dat", "wb");
}

 得到了文件指针,那我们的 fwrite 也不在话下了:

void WriteData(struct contact* c)
{
	FILE* ptr = fopen("condata.dat", "wb");
	fwrite(c->data, sizeof(struct contactmember), c->count, ptr);
}

当然,这里也有必要对 ptr 的合法性进行判断:

void WriteData(struct contact* c)
{
	FILE* ptr = fopen("condata.dat", "wb");
	if(ptr == NULL)
	{
		perror("fopen");
		return;
	}
	fwrite(c->data, sizeof(struct contactmember), c->count, ptr);
}

2.在启动程序时读取数据

很明显,我们直接把这一步放在初始化通讯录时。

其实有个问题不知道是否在困扰大家:如果本地没有这个文件,以读的形式打开文件必然是会失败的,这个时候我们如何判断程序是否是初次启动呢?解决方法很简单:不判断。

(1)你写完程序就加上这个文件,防止读不到文件

(2)先用写的方式打开文件,其目的就是为了创建文件,以保证读的正常进行

这里我选择(1),我就认为这个文件是我程序的一部分,我觉得没有毛病!

所以写的时候我们不需要考虑这个,但是 fread 如何使用?

可以发现和 fwrite 的参数是一模一样的,但是!但是!但是!重要的事情说三遍,我们不能一次性读取!我们不能一次性读取!我们不能一次性读取!原因很简单,我们把数据读取出来存放,这个时候我们所给的指针指向的是 NULL,换句话说,我们这个时候都没有开辟空间来存放数据,那读取数据的意义是什么呢?

所以我们需要保证读取数据是一个一个的读取的,并且读取的同时需要判断是否增容!

void ReadData(struct contact* c)
{
	FILE* ptr = fopen("condata.dat", "rb");
	if (ptr == NULL)
	{
		perror("fopen");
		return;
	}
}

 先打开文件获取文件指针,再判断合法性,大同小异。

while ()
	{
		if (c->count == c->capacity)
		{
			struct contactmember* temp_p = (struct contactmember*)realloc(c->data, ((c->capacity) + INC_SPACE) * sizeof(struct contactmember)); //此处提示是编译器害怕加出int范围
			if (temp_p == NULL)
			{
				printf("增容失败!\n");
				return;
			}
			else
			{
				printf("增容成功!\n");
				c->data = temp_p;
				c->capacity += INC_SPACE;
			}
		}

		fread(c->data[], sizeof(struct contactmember), 1, ptr);
	}

这是我的一些思路,首先我们一个个读取肯定要循环,但是循环什么时候结束呢?其次,我们读取数据到 c->data 中,我们需要用下标索引到具体位置,比如 c->data[0]、c->data[1],但是这个数字我们从何而来?

于是我们接下来要解决的就是这两个问题!

文件中存了多少组数据?我不知道。这会导致大麻烦,但是 fread 的返回值大家是否注意到了呢?如果读取成功了,就返回它读取到的组数,则肯定是正数。如果结束了,会返回 eof ,这样我们可以直接把 fread 写到while中判断是否要继续。(注:这里官方对 fread 说明的是" 如果发生错误或在达到计数之前遇到文件结尾,则可能小于计数 " 应该可以使用 feof 来判断结束再决定循环,这里直接放在 while 里貌似有些不合适,我会再下去测试补充的)。解决了第一个问题,第二个问题完全可以搞一个变量解决。但是这个时候不知不觉新问题又出现了:如果在 while 中就 fread 再判断增容,显然没有意义,溢出是必然的。所以不妨我们先声明一个结构体负责暂时存放数据吧!这样计数器也没必要了。

	struct contactmember datatemp = { 0 };
	while (fread(&datatemp, sizeof(struct contactmember), 1, ptr))
	{
		if (c->count == c->capacity)
		{
			struct contactmember* temp_p = (struct contactmember*)realloc(c->data, ((c->capacity) + INC_SPACE) * sizeof(struct contactmember)); //此处提示是编译器害怕加出int范围
			if (temp_p == NULL)
			{
				printf("增容失败!\n");
				return;
			}
			else
			{
				printf("增容成功!\n");
				c->data = temp_p;
				c->capacity += INC_SPACE;
			}
		}

		c->data[c->count] = datatemp;
		c->count++;
	}
}

启动程序直接打印:

发现我们存的数据都在 !

这个时候可能你就以为大功告成了,但很不幸,这些细枝末节千万不能忘记:文件既然被打开了,不用了就关闭它,手动关闭,而不是让操作系统关闭!必须要养成习惯。包括动态内存分配,也一定要释放,内存泄漏的恐惧是否支配过你呢?

	fclose(ptr);
	ptr = NULL;

也包括我们之前写的保存数据,都需要这样操作!

不由得心生感叹,多次回眸,发现自己可能没记得几个函数,最重要的却是学会了编程的思想。善用工具,看懂帮助与文献。我们学到的不一定要记住,更重要的是体会与感受。就像学数学一样,你学到的东西全部都会忘记,数学的思维却伴随你并帮助你。

Don't Give Up.

END

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Thepale2022

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

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

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

打赏作者

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

抵扣说明:

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

余额充值