通讯录需要具备的功能:
1.新增联系人
2.查找联系人
3.删除联系人
4.修改联系人
5.显示联系人
6.通讯录排序(年龄、姓名)
7.销毁通讯录
8.保存通讯录
目录
一、创建结构体
二、添加联系人
三、检查容量
四、删除联系人
五、查找联系人
六、修改联系人
七、排序
八、显示联系人
九、销毁
十、保存通讯录
10.1 fprintf版本
10.2 fwrite版本
十一、文件读到内存
11.1 fscanf版本
首先我们需要构建一个结构体,用来保存通讯录的基本信息:
#define MAX_Name 20
#define MAX_Sex 5
#define MAX_Address 20
#define MAX_People 100
struct PeopleInfo
{
char name[MAX_Name];
char sex[MAX_Sex];
int age;
int telephone;
char address[MAX_Address];
};
typedef struct Contact
{
struct PeopleInfo* arr;
//记录当前的人数
int sz;
//用来记录大小
int capacity;
}Contact;
构建后,我们需要初始化结构体:
//动态
void ContactInit(Contact* con)
{
con->arr=(struct PeopleInfo*)malloc(sizeof(struct PeopleInfo)*2);
con->sz=0;
con->capacity=2;
}
//静态
// void ContactInit(Contact* con)
// {
// con->sz=0;
// memset(con->arr,0,MAX_People*sizeof(struct PeopleInfo));
// }
构建后,我们就可以开始添加了,添加就是一个一个输入:
void ContactAdd(Contact* con)
{
checkcapacity(con);
printf("请输入姓名\n");
scanf("%s",con->arr[con->sz].name);
printf("请输入性别\n");
scanf("%s",con->arr[con->sz].sex);
printf("请输入年龄\n");
scanf("%d",&con->arr[con->sz].age);
printf("请输入电话号码\n");
scanf("%d",&con->arr[con->sz].telephone);
printf("请输入地址\n");
scanf("%s",con->arr[con->sz].address);
con->sz++;
}
既然是动态的顺序表,那么我们就需要检查容量:
void checkcapacity(Contact* con)
{
//当前个数和容量相等时意味着空间用完了,需要重新开辟
if(con->capacity==con->sz)
{
con->capacity*=2;
//先创建临时空间,以防开辟失败,导致原来的空间也消失了
struct PeopleInfo* tmp=(struct PeopleInfo*)realloc(con->arr,con->capacity*sizeof(struct PeopleInfo));
if(tmp!=NULL)
{
con->arr=tmp;
}
else
{
//开辟失败报错
perror("checkcapacity:");
}
}
}
删除功能和这个类似,找到删除的位置,然后把后面一个位置给到前一个位置,这样删除的位置就没有了:
//查找位置
//加static可以让别的文件查不到这个自定函数
static int FindLocation(Contact* con,char* name)
{
int i=0;
for(i=0;isz;i++)
{
if(strcmp(con->arr[i].name,name)==0)
{
return i;
}
}
return -1;
}
void ContactDelete(Contact* con)
{
printf("请输入要删除人的姓名\n");
char name[MAX_Name];
scanf("%s",name);
int ret=FindLocation(con,name);
if(ret==-1)
{
printf("查无此人\n");
}
else
{
int i=0;
for(i=ret;isz-1;i++)
{
con->arr[i]=con->arr[i+1];
}
con->sz--;
printf("删除成功\n");
}
}
查找功能很简单,利用刚刚的自定义函数FindLocation,很容易就能实现这个功能:
void SearchContact(Contact* con)
{
printf("请输入要查找的姓名\n");
char name[MAX_Name];
scanf("%s",name);
int ret=FindLocation(con,name);
if(ret==-1)
{
printf("查无此人\n");
}
else
{
printf("%-20s\t%-5s\t%-10s\t%-11s\t%-20s\t\n","姓名","性别","年龄","电话号码","地址");
printf("%-20s\t%-5s\t%-10d\t%-11d\t%-20s\t",
con->arr[ret].name,con->arr[ret].sex,con->arr[ret].age,con->arr[ret].telephone,con->arr[ret].address);
printf("\n");
}
}
修改也是利用FindLocation函数,然后在需要的位置重新输入就可以了:
void ModifyContact(Contact* con)
{
printf("请输入要更改的姓名\n");
char name[MAX_Name];
scanf("%s",name);
int ret=FindLocation(con,name);
if(ret==-1)
{
printf("查无此人\n");
}
else
{
printf("请输入姓名\n");
scanf("%s",con->arr[ret].name);
printf("请输入性别\n");
scanf("%s",con->arr[ret].sex);
printf("请输入年龄\n");
scanf("%d",&con->arr[ret].age);
printf("请输入电话号码\n");
scanf("%d",&con->arr[ret].telephone);
printf("请输入地址\n");
scanf("%s",con->arr[ret].address);
}
}
排序需要利用到快速排序:
//以年龄排序
int cmp_by_age(const void* e1,const void* e2)
{
return ((struct PeopleInfo*)e1)->age-((struct PeopleInfo*)e2)->age;
}
//以姓名排序
int cmp_by_name(const void* e1,const void* e2)
{
return strcmp(((struct PeopleInfo*)e1)->name,((struct PeopleInfo*)e2)->name);
}
void ContactSort(Contact* con)
{
qsort(con->arr,con->sz,sizeof(struct PeopleInfo),cmp_by_age);
//qsort(con->arr,con->sz,sizeof(struct PeopleInfo),cmp_by_name);
printf("排序成功\n");
ShowContact(con);
}
显示就是利用循环打印出来:
void ShowContact(Contact* con)
{
printf("%-20s\t%-5s\t%-10s\t%-11s\t%-20s\t\n","姓名","性别","年龄","电话号码","地址");
int i=0;
for(i=0;isz;i++)
{
printf("%-20s\t%-5s\t%-10d\t%-11d\t%-20s\t",
con->arr[i].name,con->arr[i].sex,con->arr[i].age,con->arr[i].telephone,con->arr[i].address);
printf("\n");
}
}
因为我们的空间是动态开辟的,不用的时候需要销毁:
void ContactDestroy(Contact* con)
{
free(con->arr);
con->sz=con->capacity=0;
}
保存通讯录我们就需要用到fprintf功能,把内存的数据输出到文件里,保存通讯录并不困难,难的是接下来的把文件的数据输出到终端上。
void SaveContact(struct Contact* pc)
{
//打开文件
FILE* pfW = fopen("data.txt", "w");
if (pfW == NULL)
{
perror("SaveContact::fopen");
return;
}
//写文件
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fprintf(pfW,"%s %s %d %d %s\n",pc->arr[i].name,
pc->arr[i].sex,pc->arr[i].age,pc->arr[i].telephone,pc->arr[i].address);
}
//关闭文件
fclose(pfW);
pfW = NULL;
}
void SaveContact(struct Contact* pc)
{
//打开文件
FILE* pfW = fopen("data.txt", "wb");
if (pfW == NULL)
{
perror("SaveContact::fopen");
return;
}
//写文件
int i = 0;
for (i = 0; i < pc->sz; i++)
{
//从arr[i]的位置开始读struct PeopleInfo的数据,一次读一个,读到文件中
fwrite(pc->arr+i, sizeof(struct PeopleInfo), 1, pfW);
}
//关闭文件
fclose(pfW);
pfW = NULL;
}
把文件读到内存是有点困难的,因为我们的通讯录的初始大小是2,所以文件内如果有超过2个数据我们就需要扩容。
oid LoadContact(Contact* con)
{
FILE* pfR=fopen("data.txt","r");
if(pfR==NULL)
{
perror("LoadContact::fopen");
return;
}
struct PeopleInfo tmp={0};
//fscanf要等于5,因为读5个数据,fscanf会返回5
while(fscanf(pfR,"%s %s %d %d %s",tmp.name,tmp.sex,&tmp.age,&tmp.telephone,tmp.address)==5)
{
//检查容量
checkcapacity(con);
con->arr[con->sz]=tmp;
con->sz++;
}
fclose(pfR);
pfR=NULL;
}
11.2 fread版本
void LoadContact(Contact* con)
{
FILE* pfR=fopen("data.txt","rb");
if(pfR==NULL)
{
perror("LoadContact::fopen");
return;
}
struct PeopleInfo tmp={0};
while(fread(&tmp,sizeof(struct PeopleInfo),1,pfR))
{
checkcapacity(con);
con->arr[con->sz]=tmp;
con->sz++;
}
fclose(pfR);
pfR=NULL;
}