C语言实现一个文件版动态通讯录流程详解
作者:平凡的小苏
通讯录思维导图
一、Contact.h
#include<stdio.h> #include<assert.h> #include<string.h> #include<stdlib.h> #define NAME_MAX 20 #define SEX_MAX 5 #define TELE_MAX 11 #define ADDR_MAX 30 #define INIT_CAPA 3 #define INC_CAPA 2 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char tele[TELE_MAX]; char addr[ADDR_MAX]; }PeoInfo; //动态版本 typedef struct Contact { PeoInfo* data; int sz;//表示通讯录有多少个联系人 int capacity;//表示通讯录的最大容量 }Contact; //初始化通讯录 void InitContact(Contact* pc); //添加联系人 void AddContact(Contact* pc); //删除联系人 void DelContact(Contact* pc); //显示通讯录 void ShowContact(const Contact* pc); //查找联系人 void SearchContact(Contact*pc); //修改联系人' void ModifyContact(Contact*pc); //通过名字排序联系人 void ByNameSortContact(Contact* pc); //销毁联系人 void DestroyContact(Contact* pc); //保存联系人到文本文件中 void SaveContact(Contact* pc); //从文件中读取联系人的信息 void GetContact(Contact* pc);
这些是头文件的包含,函数的声明,以及#define定义的常量,为了以后修改方便
二、Contact.c
1.初始化通讯录
void InitContact(Contact* pc)//初始化通讯录 { assert(pc!=NULL); pc-> sz = 0; pc->capacity = INIT_CAPA; PeoInfo * ptr = (PeoInfo*)calloc(INIT_CAPA, sizeof(PeoInfo)); if (ptr == NULL) { perror("InitContact"); return; } pc->data = ptr; GetContact(pc); }
初始化通讯录的容量开始为3,sz初始时为0,动态开辟的ptr开辟成功在赋给data。
2.检查容量是否满
void check_capacity(Contact* pc)//检查容量是否满 { assert(pc); if (pc->capacity == pc->sz) { PeoInfo*ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo)); if (ptr == NULL) { perror("check_capacity"); return; } pc->data = ptr; pc->capacity += INC_CAPA; printf("增容成功\n"); } }
如果容量满,则每次增容两个,如果想要增容更多,则修改define定义的常量就可以修改了。
realloc开辟的时候有可能是用pc->data来往后扩大增容,也有可能是用新的空间来开辟,为了防止开辟失败,使原有的数据丢失,则先使用新的指针变量来接收动态开辟的空间,如果开辟成功,再将它赋给data。
3.添加联系人
void AddContact(Contact* pc)//添加联系人 { assert(pc != NULL); check_capacity(pc); printf("请输入姓名:\n"); scanf("%s", pc->data[pc->sz].name); printf("请输入年龄:\n"); scanf("%d", &(pc->data[pc->sz].age)); printf("请输入性别:\n"); scanf("%s", pc->data[pc->sz].sex); printf("请输入号码:\n"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址:\n"); scanf("%s", pc->data[pc->sz].addr); printf("添加联系人成功\n"); pc->sz++; }
添加联系人前,需要先判断是否需要增容
4.显示联系人
void ShowContact(const Contact* pc)//显示联系人 { assert(pc != NULL); printf("%-20s\t%-4s\t%-4s\t%-12s\t%-30s\n","姓名","年龄","性别","电话号码","地址"); for (int i = 0; i < pc->sz; i++) { printf("%-20s\t%-4d\t%-4s\t%-12s\t%-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } }
5.查找联系人
int ByNameFind(Contact* pc, char name[])//通过名字查找联系人 { for (int i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i; } } return -1; } void SearchContact(Contact* pc)//查找联系人 { assert(pc); char name[NAME_MAX]; printf("请输入要查找的联系人\n"); scanf("%s", name); int ret = ByNameFind(pc, name); if (ret == -1) { printf("查无此人\n"); return; } printf("%-20s\t%-4s\t%-4s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话号码", "地址"); printf("%-20s\t%-4d\t%-4s\t%-12s\t%-30s\n", pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr); }
注意:名字属于字符串,名字的比较需要用strcmp来比较,如果返回-1,则是没有找到联系人,如果找到了,则就打印这个联系人的信息出来
6.修改联系人
void ModifyContact(Contact* pc)//修改联系人 { assert(pc); char name[NAME_MAX]; printf("请输入要修改的联系人姓名\n"); scanf("%s", name); int pos = ByNameFind(pc, name); if (pos == -1) { printf("找不到该联系人\n"); return; } printf("请输入姓名:\n"); scanf("%s", pc->data[pos].name); printf("请输入年龄:\n"); scanf("%d", &(pc->data[pos].age)); printf("请输入性别:\n"); scanf("%s", pc->data[pos].sex); printf("请输入号码:\n"); scanf("%s", pc->data[pos].tele); printf("请输入地址:\n"); scanf("%s", pc->data[pos].addr); printf("修改联系人成功\n"); }
注意:想要修改这个联系人,也需要通讯录中有这个联系人,所以要先查找到这个联系人,所以调用封装好的通过名字查找联系人这个函数就可以了,如果返回-1,则就是没有该联系人,无法修改,如果找到了,则才能够修改此联系人的信息。
7.通过名字来排序联系人
void ByNameSortContact(Contact* pc)//通过名字来排序 { assert(pc); if (pc->sz == 0) { printf("无联系人,无法排序\n"); return; } PeoInfo temp; for (int i = 0; i < pc->sz - 1; i++) { for(int j = 0 ;j<pc->sz-1-i;j++) { if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0) { temp = pc->data[j]; pc->data[j] = pc->data[j + 1]; pc->data[j + 1] = temp; } } } printf("排序成功\n"); }
注意:这里通过名字来排序联系人,也是需要用到strcmp这个库函数的,strcmp是一个字符来比较的,如果有一个字符大的话就要进行交换,利用的是冒泡排序思想来排序联系人的
8.保存联系人到文本文件中
void SaveContact(Contact* pc) { assert(pc); FILE* pf = fopen("Contact.txt", "wb"); if (pf == NULL) { perror("fopen"); } else { for (int i = 0; i < pc->sz; i++) { fwrite(pc->data + i, sizeof(PeoInfo), 1, pf); } fclose(pf); pf = NULL; printf("保存数据成功\n"); } }
注意:FILE是C语言标准的指针,wb是用二进制的方式写入文本文件中,而fwrite的用法我们可以利用cplusplus来查找它的用法,里面介绍了它的每个参数的用法,第一个参数ptr其实就是传我们的data的指针进去,第二个参数本质就是求大小的,求得是PeoInfo得大小,第三个参数就是每次添加几个联系人,每次添加1个。第四个参数是把文件流放入
9.从文件中读取联系人的信息
//从文件中初始化联系人 void GetContact(Contact* pc) { assert(pc); FILE* pf = fopen("Contact.txt", "rb"); if (pf == NULL) { perror("GetContact::fopen"); } else { PeoInfo ptr = { 0 }; int i = 0; while (fread(&ptr, sizeof(PeoInfo), 1, pf)) { check_capacity(pc); pc->data[i] = ptr; i++; pc->sz++; } fclose(pf); pf = NULL; } }
注意:rb是利用二进制来读取联系人的,fread的用法如上图所示。
10.销毁联系人
//销毁联系人 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; }
注意:在我们退出通讯录时,要将它进行销毁
三、text.c
#include"Contact.h" void menu() { printf("**********************************\n"); printf("*******1.Add 2.Del ********\n"); printf("*******3.Search 4.Modify********\n"); printf("*******5.Show 6.Sort ********\n"); printf("*******0.exit ********\n"); printf("**********************************\n"); } enum Option { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT }; int main() { Contact con; InitContact(&con); int input = 0; do { menu(); printf("请输入数字:\n"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DEL : DelContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyContact(&con); break; case SHOW: ShowContact(&con); break; case SORT: ByNameSortContact(&con); break; case EXIT: SaveContact(&con); DestroyContact(&con); printf("退出程序\n"); break; default: printf("输入错误,请重新输入\n"); break; } } while (input); return 0;
注意:首先要建立菜单,然后用枚举来定义case后面,这样为了可以让我们看代码更清晰,在运行程序时要将联系人从文件中读取出来,在退出文件时,要把联系人保存到文本文件中去,在进行销毁。
好了,小编的分享到这里就结束了,如果有什么不足的地方请大佬多多指教!!!
到此这篇关于C语言实现一个文件版动态通讯录流程详解的文章就介绍到这了,更多相关C语言动态通讯录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!