目录
1. 需求分析
通过C语言实现简单的通讯录管理系统,要求实现的功能有:增加联系人信息、删除联系人信息、查找联系人信息、修改联系人信息、清空联系人信息、对联系人的信息进行排序、显示联系人信息,同时要求使用动态内存分配和文件管理,实现通讯录的保存与对读取。
2. 程序架构
程序分为test.c、contact.c两个源文件和contact.h一个头文件。
test.c:主函数接口引入。
contact.c:函数主要实现
contact.h:头文件引入、函数声明、结构体声明。
3. 代码实现(分函数呈现)
(1)主函数代码实现
//测试函数的功能 int main() { //创建通讯录 struct contact con;//con就是通讯录,里面包含:data指针和size,capacity //初始化通讯录 Init_Contact(&con); int input = 0; int size = 0;//记录当前有多少人 do { menu(); printf("请选择->"); scanf("%d", &input); switch (input) { case ADD: //增加 Add_Contacter(&con); break; case DEL: //删除 Del_Contacter(&con); break; case SEARCH: //查找 Find_Contacter(&con); break; case MODIFY: //修改 Mod_Contacter(&con); break; case SHOW: //打印 Print_Contacter(&con); break; case CLEAR: //清空 Clear_Contacter(&con); break; case SORT: //排序 Sort_Contacter(&con); break; case EXIT: //退出并销毁通讯录 Save_Contact(&con); Destory_Contact(&con); printf("退出通讯录!\n"); break; case 8: //保存通讯录 Save_Contact(&con); break; default: printf("选择错误,请重新输入!\n"); } } while (input); return 0; }
分析:主要是引入函数的接口,此处用了常见的do while循环,并且将input变量作为while()后面括号中的条件,此处是一个极其巧妙的运用,当input变量为0时循环结束,程序终止,此时也与前面的switch和case进行了一一对应。这个版本的通讯录管理系统与之前的通讯录管理系统略有区别。此处的区别在于创建通讯录的时候,此处创建的是一个结构体变量con,而不是像之前一样建立的是一个结构体数组,此处结构体变量中存储的一方面有通讯录中所有人的信息即data,这些信息打包成了一个数组,同时还有两个整型变量,size存储的是当前已经有的元素个数,而capacity存储的是当前通讯录的最大容量。相比之前的动态管理版本,此处增加了保存选项,同时在退出通讯录之前都会默认进行一次保存,以防用户在退出之前对通讯录的信息忘记进行保存。
(2)菜单函数的实现
void menu() { printf("************************************\n"); printf("**** 1.add 2.del ****\n"); printf("**** 3.search 4.modify ****\n"); printf("**** 5.show 6.clear ****\n"); printf("**** 7.sort 8.save ****\n"); printf("********* 0.exit **********\n"); printf("************************************\n"); }
分析:简单运用printf将菜单进行输出,通过*来使输出形式更加清晰简洁美观。
(3)初始化功能实现
//加载文件中的信息到通讯录中 void Load_Contact(struct contact* ps) { FILE* pfRead = fopen("contact.dat", "rb"); if (pfRead== NULL) { printf("Load_Contact::%s", strerror(errno)); return; } //读取文件存放到通讯录中 struct PeoInfo tmp = { 0 }; while (fread(&tmp,sizeof(tmp),1,pfRead)) { CheckCapacity(ps); ps->data[ps->size] = tmp; ps->size++; printf("读取成功!\n"); } //关闭文件 fclose(pfRead); pfRead = NULL; } //初始化通讯录中的信息 void Init_Contact(struct contact* ps) { ps->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3); if (ps->data == NULL) { return; } ps->size = 0;//设置通讯录最初只有0个元素 ps->capacity = DEFAULT_SZ; //把文件中已经存放的信息加载到通讯录中 Load_Contact(ps); }
分析:在此处就体现了和之前通讯录管理系统的不同,此处增加了一个加载函数,是将之前文件中联系人的信息加载到通讯录中来,使用了文件操作函数fread()函数来对文件中的信息进行读取,此处使用了fread()函数的返回值,即成功读取的数据的数目,因为每次只读取一个联系人的数据,所以如果成功读取就返回1,如果读取失败就返回0,当数据读取完时即会读取失败,此时通过while()循环进行控制,同时使用fopen()和fclose()函数进行控制文件的打开和关闭。
读取时要注意一个问题,就是当我们动态开辟的空间不够时,我们就要使用我们的CheckCapacity()函数对容量进行检查,如果容量不够就要进行扩容。
(4)添加联系人功能实现
void CheckCapacity(struct contact* ps) { if (ps->size == ps->capacity) { //增容 struct PeoInfo* ptr= realloc(ps->data, (ps->capacity + 2)*sizeof(struct PeoInfo)); if (ptr != NULL) { ps->data = ptr; ps->capacity += 2; printf("增容成功!\n"); } else { printf("增容失败!\n"); } } } //添加通讯录中的信息 void Add_Contacter(struct contact* ps) { //检测当前通讯录的容量 //1、如果满了,就增加空间 //2、如果不满,啥事都不干 CheckCapacity(ps); //增加数据 printf("请输入名字:"); scanf("%s", &ps->data[ps->size].name); printf("请输入年龄:"); scanf("%d", &ps->data[ps->size].age); printf("请输入性别:"); scanf("%s", &ps->data[ps->size].sex); printf("请输入电话:"); scanf("%s", &ps->data[ps->size].phone); printf("请输入地址:"); scanf("%s", &ps->data[ps->size].address); ps->size++; printf("添加成功!\n"); }
分析:在进行添加时要注意进行分情况讨论,首先就是当联系人数目满了的话就必须对其进行扩容,此处检查容量运用的是CheckCapacity函数,每次扩容可以增加两个联系人的信息,通过realloc函数进行实现,扩容后,容量加2。只有当联系人数目未满的时候才能继续添加,添加后,要将记录联系人数目的变量size进行加一;如果当前通讯录没有满,就什么都不干,即进行正常的添加通讯录联系人的信息就好。此处并不需要每一次在添加联系人信息后
(5)删除通讯录中的信息
//删除通讯录中的信息 void Del_Contacter(struct contact* ps) { char name[MAX_NAME]; printf("请输入你要删除的联系人的姓名:"); scanf("%s", name); //查找要删除的人所在的位置 //找到了返回名字所在元素的下标,没找到就返回-1 int pos = Find_byName(ps,name); if (pos==-1)//删除的人不存在 { printf("要删除的人不存在!\n"); } else//删除 { int j = 0; for (j = pos; j < ps->size-1; j++) { ps->data[j] = ps->data[j + 1]; } ps->size--; printf("删除成功!\n"); } }
分析:删除操作并不复杂,就是先查找到我们想要删除的那个联系人,然后将这个联系人后面的信息逐个向前进行覆盖,同时将记录联系人数目的变量size的值进行减1。
(6)查找通讯录中联系人的信息
//查找通讯录中的信息 void Find_Contacter(const struct contact* ps) { char name[MAX_NAME]; printf("请输入你要查找的联系人的姓名:"); scanf("%s", name); int pos = Find_byName(ps, name); if (pos==-1)//查找的人不存在 { printf("要查找的人不存在!\n"); } else { printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n", ps->data[pos].name, ps->data[pos].age, ps->data[pos].sex, ps->data[pos].phone, ps->data[pos].address); } }
分析:首先先通过查找函数将我们想要查找到的联系人找到,然后通过printf函数打印联系人的信息。
(7)查找函数实现
static int Find_byName(const struct contact* ps, char name[MAX_NAME]) { int i = 0; for (i = 0; i < ps->size; i++) { if (0 == strcmp(ps->data[i].name, name)) { return i; } } return -1; }
分析:此处查找函数是通过遍历联系人中的信息进行实现的,当我们想查找的联系人的信息与某个联系人的信息一致时就停止下来,如果能够找到就返回其对应的下标,如果找不到就返回-1。
(8)修改联系人的信息
//修改通讯录中的信息 void Mod_Contacter(struct contact* ps) { char name[MAX_NAME]; printf("请输入你要修改的联系人的姓名:"); scanf("%s", name); int pos = Find_byName(ps, name); if (pos==-1)//修改的人不存在 { printf("要修改的人不存在!\n"); } else { printf("请输入名字:"); scanf("%s", &ps->data[pos].name); printf("请输入年龄:"); scanf("%d", &ps->data[pos].age); printf("请输入性别:"); scanf("%s", &ps->data[pos].sex); printf("请输入电话:"); scanf("%s", &ps->data[pos].phone); printf("请输入地址:"); scanf("%s", &ps->data[pos].address); printf("修改成功!\n"); } }
分析:修改联系人的信息与上面其实类似,首先是找到我们想要修改的联系人的信息,然后再进行修改。
(9)打印通讯录中联系人的信息
//打印通讯录中的信息 void Print_Contacter(const struct contact* ps) { if (ps->size == 0) { printf("通讯录为空!\n"); } else { //标题 printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址"); int i = 0; while (i < ps->size) { //数据 printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].phone, ps->data[i].address); i++; } } }
分析:这个功能并不难实现,就是设置一个循环变量i,然后从头开始对通讯录信息进行遍历,当i小于size的时候将对应联系人的信息打印出来。
(10)对通讯录中联系人的信息进行排序
//对通讯录中的信息进行排序 //排序函数 //1.按照姓名进行排序 int Conpare_ByName(const void *e1,const void *e2) { return strcmp(((struct PeoInfo*)e1)->name, ((struct PeoInfo*)e2)->name); } //2.按照年龄进行排序 int Conpare_ByAge(const void* e1, const void* e2) { return ((struct PeoInfo*)e1)->age-((struct PeoInfo*)e2)->age; } //3.按照住址进行排序 int Conpare_ByAddress(const void* e1, const void* e2) { return strcmp(((struct PeoInfo*)e1)->address, ((struct PeoInfo*)e2)->address); } void Sort_Contacter(struct contact* ps) { printf("请选择你想排序的方式:\n"); printf("1.姓名\n2.年龄\n3.住址\n"); int input = 0; scanf("%d", &input); switch (input) { case 1: qsort(ps->data,ps->size,sizeof(ps->data[0]),Conpare_ByName); printf("排序成功\n"); break; case 2: qsort(ps->data, ps->size, sizeof(ps->data[0]), Conpare_ByAge); printf("排序成功\n"); break; case 3: qsort(ps->data, ps->size, sizeof(ps->data[0]), Conpare_ByAddress); printf("排序成功\n"); break; } }
分析:此处运用了qsort快速排序的函数,此处的关键就是设定好自己想要排序的依据,比如我们想根据年龄或者名字进行排序,此处要求掌握快速排序函数的使用。
(11)清空通讯录中联系人的信息
//清空通讯中的信息 void Clear_Contacter(struct contact* ps) { memset(ps->data, 0, sizeof(ps->data)); ps->size = 0; printf("清空成功!\n"); }
分析:此处其实类似于初始化联系人的信息,就是将那段内存空间中的数据使用memset函数全部设置为0,然后将记录联系人数目的变量size设定为0。
(12)销毁通讯录
//销毁通讯录 void Destory_Contact(struct contact* ps) { free(ps->data); ps->data = NULL; }
(13)保存通讯录中的信息
//保存通讯录的信息 void Save_Contact(struct contact* ps) { FILE* pfWrite = fopen("contact.dat", "wb"); if (pfWrite == NULL) { printf("Save_Contact::%s", strerror(errno)); return; } //将通讯录中的数据到文件中 int i = 0; for (i = 0; i < ps->size; i++) { fwrite(&(ps->data[i]), sizeof(struct PeoInfo), 1, pfWrite); } printf("保存成功!\n"); //关闭文件 fclose(pfWrite); pfWrite = NULL; }
分析:保存通讯录中的信息,使用的是fwrite()函数,即将通讯录中的数据写入到文件中去,同时使用了fopen()和fclose()进行关闭文件。值得注意的是:最后要将文件指针置为空指针。
4. 代码呈现(分文件实现)
(1)test.c
#include"contact.h" enum Option { EXIT,//0 ADD,//1 DEL,//2 SEARCH,//3 MODIFY,//4 SHOW,//5 CLEAR,//6 SORT,//7 SAVE }; void menu() { printf("************************************\n"); printf("**** 1.add 2.del ****\n"); printf("**** 3.search 4.modify ****\n"); printf("**** 5.show 6.clear ****\n"); printf("**** 7.sort 8.save ****\n"); printf("********* 0.exit **********\n"); printf("************************************\n"); } //测试函数的功能 int main() { //创建通讯录 struct contact con;//con就是通讯录,里面包含:data指针和size,capacity //初始化通讯录 Init_Contact(&con); int input = 0; int size = 0;//记录当前有多少人 do { menu(); printf("请选择->"); scanf("%d", &input); switch (input) { case ADD: //增加 Add_Contacter(&con); break; case DEL: //删除 Del_Contacter(&con); break; case SEARCH: //查找 Find_Contacter(&con); break; case MODIFY: //修改 Mod_Contacter(&con); break; case SHOW: //打印 Print_Contacter(&con); break; case CLEAR: //清空 Clear_Contacter(&con); break; case SORT: //排序 Sort_Contacter(&con); break; case EXIT: //退出并销毁通讯录 Save_Contact(&con); Destory_Contact(&con); printf("退出通讯录!\n"); break; case 8: //保存通讯录 Save_Contact(&con); break; default: printf("选择错误,请重新输入!\n"); } } while (input); return 0; }
(2)contact.h
#pragma once #define MAX_NAME 20 #define MAX_PHONE 12 #define MAX_SEX 5 #define MAX_ADDRESS 30 #define _CRT_SECURE_NO_WARNINGS 1 #define DEFAULT_SZ 3 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<errno.h> struct PeoInfo { char name[MAX_NAME]; char phone[MAX_PHONE]; char sex[MAX_SEX]; char address[MAX_ADDRESS]; int age; }; struct contact { struct PeoInfo* data; int size;//记录当前已经有的元素个数 int capacity;//记录当前通讯录的最大容量 }; //声明函数 //初始化通讯录的函数 void Init_Contact(struct contact* ps); //增加一个信息到通讯录 void Add_Contacter(struct contact* ps); //删除指定的联系人 void Del_Contacter(struct contact* ps); //查找指定人的信息 void Find_Contacter(const struct contact* ps); //修改指定人的信息 void Mod_Contacter(struct contact* ps); //打印通讯录中的信息 void Print_Contacter(const struct contact* ps); //对通讯录中的联系人进行排序 void Sort_Contacter(struct contact* ps); //清空通讯录中的信息 void Clear_Contacter(struct contact* ps); //销毁通讯录 void Destory_Contact(struct contact*ps); //保存通讯录的信息 void Save_Contact(struct contact* ps); //加载文件中的信息到通讯录中 void Load_Contact(struct contact* ps);
(3)contact.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"contact.h" //实现函数的功能 static int Find_byName(const struct contact* ps, char name[MAX_NAME]) { int i = 0; for (i = 0; i < ps->size; i++) { if (0 == strcmp(ps->data[i].name, name)) { return i; } } return -1; } void CheckCapacity(struct contact*); //加载文件中的信息到通讯录中 void Load_Contact(struct contact* ps) { FILE* pfRead = fopen("contact.dat", "rb"); if (pfRead== NULL) { printf("Load_Contact::%s", strerror(errno)); return; } //读取文件存放到通讯录中 struct PeoInfo tmp = { 0 }; while (fread(&tmp,sizeof(tmp),1,pfRead)) { CheckCapacity(ps); ps->data[ps->size] = tmp; ps->size++; printf("读取成功!\n"); } //关闭文件 fclose(pfRead); pfRead = NULL; } //初始化通讯录中的信息 void Init_Contact(struct contact* ps) { ps->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3); if (ps->data == NULL) { return; } ps->size = 0;//设置通讯录最初只有0个元素 ps->capacity = DEFAULT_SZ; //把文件中已经存放的信息加载到通讯录中 Load_Contact(ps); } void CheckCapacity(struct contact* ps) { if (ps->size == ps->capacity) { //增容 struct PeoInfo* ptr= realloc(ps->data, (ps->capacity + 2)*sizeof(struct PeoInfo)); if (ptr != NULL) { ps->data = ptr; ps->capacity += 2; printf("增容成功!\n"); } else { printf("增容失败!\n"); } } } //添加通讯录中的信息 void Add_Contacter(struct contact* ps) { //检测当前通讯录的容量 //1、如果满了,就增加空间 //2、如果不满,啥事都不干 CheckCapacity(ps); //增加数据 printf("请输入名字:"); scanf("%s", &ps->data[ps->size].name); printf("请输入年龄:"); scanf("%d", &ps->data[ps->size].age); printf("请输入性别:"); scanf("%s", &ps->data[ps->size].sex); printf("请输入电话:"); scanf("%s", &ps->data[ps->size].phone); printf("请输入地址:"); scanf("%s", &ps->data[ps->size].address); ps->size++; printf("添加成功!\n"); } //删除通讯录中的信息 void Del_Contacter(struct contact* ps) { char name[MAX_NAME]; printf("请输入你要删除的联系人的姓名:"); scanf("%s", name); //查找要删除的人所在的位置 //找到了返回名字所在元素的下标,没找到就返回-1 int pos = Find_byName(ps,name); if (pos==-1)//删除的人不存在 { printf("要删除的人不存在!\n"); } else//删除 { int j = 0; for (j = pos; j < ps->size-1; j++) { ps->data[j] = ps->data[j + 1]; } ps->size--; printf("删除成功!\n"); } } //查找通讯录中的信息 void Find_Contacter(const struct contact* ps) { char name[MAX_NAME]; printf("请输入你要查找的联系人的姓名:"); scanf("%s", name); int pos = Find_byName(ps, name); if (pos==-1)//查找的人不存在 { printf("要查找的人不存在!\n"); } else { printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n", ps->data[pos].name, ps->data[pos].age, ps->data[pos].sex, ps->data[pos].phone, ps->data[pos].address); } } //修改通讯录中的信息 void Mod_Contacter(struct contact* ps) { char name[MAX_NAME]; printf("请输入你要修改的联系人的姓名:"); scanf("%s", name); int pos = Find_byName(ps, name); if (pos==-1)//修改的人不存在 { printf("要修改的人不存在!\n"); } else { printf("请输入名字:"); scanf("%s", &ps->data[pos].name); printf("请输入年龄:"); scanf("%d", &ps->data[pos].age); printf("请输入性别:"); scanf("%s", &ps->data[pos].sex); printf("请输入电话:"); scanf("%s", &ps->data[pos].phone); printf("请输入地址:"); scanf("%s", &ps->data[pos].address); printf("修改成功!\n"); } } //打印通讯录中的信息 void Print_Contacter(const struct contact* ps) { if (ps->size == 0) { printf("通讯录为空!\n"); } else { //标题 printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址"); int i = 0; while (i < ps->size) { //数据 printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].phone, ps->data[i].address); i++; } } } //对通讯录中的信息进行排序 //排序函数 //1.按照姓名进行排序 int Conpare_ByName(const void *e1,const void *e2) { return strcmp(((struct PeoInfo*)e1)->name, ((struct PeoInfo*)e2)->name); } //2.按照年龄进行排序 int Conpare_ByAge(const void* e1, const void* e2) { return ((struct PeoInfo*)e1)->age-((struct PeoInfo*)e2)->age; } //3.按照住址进行排序 int Conpare_ByAddress(const void* e1, const void* e2) { return strcmp(((struct PeoInfo*)e1)->address, ((struct PeoInfo*)e2)->address); } void Sort_Contacter(struct contact* ps) { printf("请选择你想排序的方式:\n"); printf("1.姓名\n2.年龄\n3.住址\n"); int input = 0; scanf("%d", &input); switch (input) { case 1: qsort(ps->data,ps->size,sizeof(ps->data[0]),Conpare_ByName); printf("排序成功\n"); break; case 2: qsort(ps->data, ps->size, sizeof(ps->data[0]), Conpare_ByAge); printf("排序成功\n"); break; case 3: qsort(ps->data, ps->size, sizeof(ps->data[0]), Conpare_ByAddress); printf("排序成功\n"); break; } } //清空通讯中的信息 void Clear_Contacter(struct contact* ps) { memset(ps->data, 0, sizeof(ps->data)); ps->size = 0; printf("清空成功!\n"); } //销毁通讯录 void Destory_Contact(struct contact* ps) { free(ps->data); ps->data = NULL; } //保存通讯录的信息 void Save_Contact(struct contact* ps) { FILE* pfWrite = fopen("contact.dat", "wb"); if (pfWrite == NULL) { printf("Save_Contact::%s", strerror(errno)); return; } //将通讯录中的数据到文件中 int i = 0; for (i = 0; i < ps->size; i++) { fwrite(&(ps->data[i]), sizeof(struct PeoInfo), 1, pfWrite); } printf("保存成功!\n"); //关闭文件 fclose(pfWrite); pfWrite = NULL; }