模拟实现通讯录<三> (文件流)

# 运用文件操作函数实现通讯录

/*

  文件流实现通讯录说明:   我们平时测试代码输入数据,但是每次一旦结束程序,数据也就没有了, 比如测试通讯

录, 输入很多信息后发现有个地方需要修改,就要结束程序,下一次继续重新输入,很麻烦对不对, 文件流实现的

好处就是可以每次将我们输入的数据保存到一个文件中, 下一次直接从文件中读取,省了不少麻烦!

*/


@ 模拟实现通讯录<一> (静态实现)博客链接:http://blog.csdn.net/bitboss/article/details/51374654

@ 模拟实现通讯录<二> (动态实现)博客链接:http://blog.csdn.net/bitboss/article/details/51417430


<一>  代码中用到的文件操作函数 (只对函数功能简介,函数参数部分详情请自己了解);


@ fopen函数简介:头文件<stdlib.h>;


函数原型: FILE * fopen(const char * path,const char * mode);

<1> path 是所要打开的文件名, mode 是以何种形式打开(mode有多种形式);

<2> 打开文件后要有与之对应的 fclose(const char *path)函数关闭文件;

返回值: 文件顺利打开后, 向该流的 文件指针 就会被返回。如果文件打开失败则返回NULL,并把 错误代码 存在

errno 中。

一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以

一般在fopen()后作错误判断及处理。


@ perror函数简介:头文件<stdlib.h>;

        perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再

加上错误原因字符串。此错误原因依照全局变量errno(这里的说法不 准确,errno是一个宏,该宏返回左值) 的值来决定要

输出的字符串。
       
     在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已

经重新设置了errno的值。perror函数只是将你输入的一些信息 和现在的errno所对应的错误一起输出。


@ fwrite函数简介:头文件<stdlib.h>;

函数原型: size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);

函数功能:fwrite是C语言函数,指向文件写入一个数据块。

注意:这个函数以二进制形式对文件进行操作,不局限于文本文件

返回值:返回实际写入的 数据块 数目

(1)buffer:是一个 指针 ,对fwrite来说,是要获取数据的地址;
(2)size:要写入内容的单字节数;
(3)count:要进行写入size字节的 数据项 的个数;
(4)stream:目标 文件指针
(5)返回实际写入的数据项个数count。


@ fread 函数简介:头文件<stdlib.h>;

函数原型: size_t fread ( void * buffer , size_t size , size_t count , FILE * stream ) ;

函数功能:

       fread是一个函数。从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读

取到 的元素个数,如果不成功或读到文件末尾返回 0。


参数:

buffer
用于接收数据的 内存地址

size
要读的每个数据项的字节数 ,单位是 字节

count
要读 count个数据项,每个数据项size个字节.

stream
输入流

fread返回值

     实际读取的元素个数。如果返回值与 count 不相同,则可能文件结尾或发生错误。从ferror和feof获取错误信息或检

测是否到达文件结尾。  

文件的创建位置可以是在以下相对路径创建,也可以在任意位置创建,但是调用文件名时就必须是绝对路径,同时注

意 ‘\’ 需要进行转义; 例如:C:\Intel\Logs   就必须写为 C:\\Intel\\Logs;




<二>  代码实现

# 自定义头文件 “address.h”部分

#pragma warning(disable:4996)//消除scanf的警告;

#ifndef __ADDRESS_H__//防止重定义;
#define __ADDRESS_H__

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


#define NAME_MAX 20
#define SEX_MAX 4
#define HOME_MAX 10
#define TEL_MAX 13
#define MEMBER_MAX 2//刚开始电话本的容量

//姓名、性别、年龄、电话、住址
typedef struct person
{
	char name[NAME_MAX ];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX ];
	char home[HOME_MAX ];
}person;

//通讯录成员结构体;
typedef struct count
{
	person *people;
	int size;//确定通讯录当前人数;
	int capacity ; //电话本容量;
}count,*con;//指针用来在函数内部接受结构体指针;

enum op
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MOD,
	SHOW,
	SORT,
	EMPTY
};
void Exit(con Con);//退出函数;
void Add(con Con);//新增成员;
void Del(con Con);//删除成员;
void Search(con Con);//查找成员;
void Mod(con Con);//修改成员;
void Show(con Con);//显示所有成员;
void Empty(con Con);//清空通讯录;
void Sort(con Con);//排序所有成员;
void Check(con Con);//选择函数;
void _Print();
void SaveData (con Con);//存入数据保存到文件流;
void DownLoad(con Con);//从文件流中读取已经保存的数据;

//void (*str[8])(con Con) = {Exit,Add,Del,Search,Mod,Show,Sort,Empty};
//函数指针数组,比switch语句方便很多!

#endif  //__ADDRESS_H__


#  主函数main 部分

#include"address.h"

int main()
{
	count Con = {0};
	Con.size = 0;//size一定要初始化为 0;
	Con.capacity  = MEMBER_MAX ;

	Con.people  = (person *)malloc(MEMBER_MAX *sizeof(person));
	/*
	注意与malloc对应的free();  本题直接在推出功能中进行free;
	*/
	if(Con.people == NULL)//是否开辟成功的判断;
		printf("out of memory\n");
	
	else
	{
		    DownLoad(&Con);//从文件读取数据;
			while(1)
			{
				Check(&Con);
			}
	}

	system("pause");
	return 0;
}


# 函数功能实现部分

#include"address.h"
//姓名、性别、年龄、电话、住址
void _Print()
{	
	printf("*************************\n");
	printf("1.	添加联系人信息\n");
    printf("2.	删除指定联系人信息\n");
    printf("3.	查找指定联系人信息\n");
    printf("4.	修改指定联系人信息\n");
    printf("5.	显示所有联系人信息\n");
	printf("6.	排序所有联系人信息\n");
    printf("7.	清空所有联系人\n");
	printf("0.      退出\n");
	printf("*************************\n");
	printf("请选择功能 (0-7):>\n");
}

/*
因为好几个函数都会用到查找函数,所以单独封住一个find函数;
*/

void Judge_Full(con Con)
{
	if(Con->size == Con->capacity )
	{
		/*
		当前人数已经达到最大通讯录容量,则进行扩容,用realloc函数;
		*/
		person *tmp = (person *)realloc (Con->people,(Con->capacity +3)*sizeof(person));
		if(tmp == NULL)
			printf("out of memory\n");
		else
			Con->people = tmp; 

		(Con->capacity)  += 3;
	}//判断通讯录是否溢出;
}

int find(con Con, char arr[])//公用查找函数;
{
	int i =0;
	for(;i<Con->size;i++)
	{
		if(strcmp(Con->people [i].name,arr) == 0)
			return i;
	}
	return -1;
}

void Add(con Con)//增加成员;
{
	if(Con->size == Con->capacity )
	{
		/*
		当前人数已经达到最大通讯录容量,则进行扩容,用realloc函数;
		*/
		person *tmp = (person *)realloc (Con->people,(Con->capacity +3)*sizeof(person));
		if(tmp == NULL)
			printf("out of memory\n");
		else
			Con->people = tmp; 

		(Con->capacity)  += 3;
	}//判断通讯录是否溢出;

	printf("准备添加一个新成员:>\n");
	printf("请输入姓名:\n");
	scanf("%s",&(Con->people[Con->size].name ));
		printf("请输入性别:\n");
	scanf("%s",&(Con->people[Con->size].sex ));
		printf("请输入年龄:\n");
	scanf("%d",&(Con->people[Con->size].age));
		printf("请输入电话:\n");
	scanf("%s",&(Con->people[Con->size].tel));
		printf("请输入地址:\n");
	scanf("%s",&(Con->people[Con->size].home));

	printf("保存成功!\n");

	Con->size++;//注意:每增加一个成员,通讯录总人数size都得+1;
}

void Del(con Con)//删除成员;
{   
	
	int ret = 0;
	char name[20] = {0};

	if(Con->size == 0)
	{
		printf("亲!当前通讯录是空的!\n");
		return ;
	}

	printf("请输入需要删除人的姓名:\n");
	scanf("%s",&name);

	ret = find(Con,name);
	/*
	删除的方法是将最后一个成员放到删除的这个成员位置上;
	注意: 不能忘记size--;
	*/
	if(ret>=0)
	{
		Con->people[ret] = Con->people[Con->size-1];
		(Con->size)--;
		printf("删除成功!\n");
		
	}
	else
		printf("没有找到要删除的对象!\n");
	
}



void Search(con Con)//查找成员;直接调用find函数;
{
	int ret = 0;
	char name[20] = {0};

	printf("请输入要查询人的姓名:\n");
	scanf("%s",&name);

	ret = find(Con,name);
	if(ret>=0)
	{
printf("%10s\t%4s\t%3s\t%13s\t%10s\n","name","sex","age","tel","address");

printf("%10s\t%4s\t%3d\t%13s\t%10s\n",Con->people [ret].name ,
	   Con->people [ret].sex  ,Con->people [ret].age,
	   Con->people [ret].tel ,Con->people [ret].home );
	}
	else
		printf("没找到该通讯录好友!\n");
	

}

void Mod(con Con)//改变成员;
{
	int i = 0;
	int ret = 0;
	char name[20] = {0};
	char *ptr[] = {"name","sex","age","tel","address"};
	//每次改变成员的单个内容;
	printf("请输入需要修改的联系人:\n");
	scanf("%s",&name);

	ret = find(Con,name);//调用查找函数;
	
	if(ret>=0)
	{
		int ages = 0;
		char mod[20] = {0};
		int sel = 0;
		printf("请输入需要修改的选项:\n");
		printf("1.name\n2.sex\n3.age\n4.tel\n5.adress\n");
		scanf("%d",&sel);
		printf("请输入\n");
		//这块选用switch语句;比较容易看懂;其实可以进行封装;
		switch(sel)
		{
		case 1:
			scanf("%s",&mod);
			strcpy(Con->people [ret].name,mod);
			printf("修改成功!\n");
			break;
		case 2:
						scanf("%s",&mod);
			strcpy(Con->people [ret].sex ,mod);
			printf("修改成功!\n");
			break;
		case 3:
						scanf("%d",&ages);
			Con->people [ret].age  = ages;
			printf("修改成功!\n");
			break;
		case 4:
						scanf("%s",&mod);
			strcpy(Con->people [ret].tel ,mod);
			printf("修改成功!\n");
			break;
		case 5:
						scanf("%s",&mod);
			strcpy(Con->people [ret].home ,mod);
			printf("修改成功!\n");
			break;
		default:
			printf("修改失败!\n");
			break;
		}
	}
	else
		printf("没有该成员!\n");

}


void Show(con Con)//显示所有成员;
{
	int i = 0;
   printf("%10s\t%4s\t%3s\t%13s\t%10s\n","name","sex","age","tel","address");
	//循环打印每个通讯录成员;
	for(; i < Con->size ; i++)
	{
		printf("%10s\t%4s\t%3d\t%13s\t%10s\n",Con->people [i].name ,
	   Con->people [i].sex  ,Con->people [i].age,
	   Con->people [i].tel ,Con->people [i].home );
	}

}

void Empty(con Con)//清空联系人;
{
	/*
	清空通讯录只需要将通讯录总成员数size改为 0;
	*/
	Con->size = 0;
	printf("通讯录已空!\n");
}


void Sort(con Con)//排序所有联系人;
{
 /*
 冒泡排序所有成员的名字;
 */
	int flag = 0;
	int i = 0;
	int j = 0;
	for(i = 0; i < Con->size-1 ;i++)
	{
		flag = 1;
		for(j = 0;j <(Con->size) - i -1;j++)
		{
			if((strcmp(Con->people[j].name , Con->people [j+1].name))> 0)
			{
				person tmp = Con->people[j];
				Con->people[j] = Con->people[j+1];
				Con->people[j+1] = tmp;
				flag = 0;
			}
		}			
		if(flag==1)
				break;
	}
}

void Exit(con Con)//退出函数;
{
	SaveData(Con);
	free(Con->people );
	Con->people = NULL;
	exit(0);
}

//定义一个函数指针数组,方便对函数的调用!
void (*str[8])(con Con) = {Exit,Add,Del,Search,Mod,Show,Sort,Empty};


void Check(con Con)//选择函数;
{	
	int select = '0';
	
	//if(Con->size == Con->capacity )
	//{
	//	person *tmp = (person *)realloc (Con->people ,Con->capacity +3);
	//	if(tmp == NULL)
	//		printf("out of memory\n");
	//	else
	//		Con->people = tmp; 

	//	Con->capacity  += 3;
	//}
	_Print();
	scanf("%d",&select);


	if(select==0 || select==1|| select==2|| select==3|| select==4|| select==5|| select==6 || select==7)
	         str[select](Con);
	/*
	如果在main函数中不进行while循环直接在Check函数中调用自己;
	缺陷是:反复递归调用Check函数,会导致栈溢出;
	Check(Con);
	*/
	else
	{
		printf("输入格式错误!\n");
	}
}


void SaveData(con Con)//保存数据到文件流
{
	/*
	SaveData 的调用放在退出程序之前;
	*/
	int i = 0;
	FILE *pfwrite = fopen("message.txt","w");
	if(pfwrite  == NULL)
	{
		perror("open file for write");
		exit(EXIT_FAILURE);
	}
	for(i = 0; i < Con->size ; i++)
	{
		fwrite(&(Con->people[i]), sizeof(person), 1, pfwrite);
	}
	fclose(pfwrite);
}


void DownLoad(con Con)//从文件里读取数据
{//DownLoad 的调用在刚创建好Con之后; main函数部分;
	/*
	这里临时创建一个tmp的目的在于,防止因为没有增容而导致的溢出;
	所以我们临时创建一个tmp,将从文件中读取的内容放在tmp中,然后判断当前
	是否需要增容; 之后再将tmp的内容放入我们的数据块中;
	*/
	int i = 0;
	person tmp = {0};

	FILE *pfread = fopen("message.txt","r");
	if(pfread  == NULL)
	{
		perror("open file for read");
		exit(EXIT_FAILURE);
	}

	while(fread(&tmp,sizeof (person), 1, pfread))
	{
		Judge_Full(Con);
		Con->people [i] = tmp;
		i++;
		Con->size ++;//每读入一个数据,当前数据数目size就增加一个;
	}
}

Ps:  不逼自己一把,怎么能知道自己有多优秀!

Thanks!



  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值