C语言实现一个文件版动态通讯录流程详解
平凡的小苏 人气:0通讯录思维导图
一、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后面,这样为了可以让我们看代码更清晰,在运行程序时要将联系人从文件中读取出来,在退出文件时,要把联系人保存到文本文件中去,在进行销毁。
好了,小编的分享到这里就结束了,如果有什么不足的地方请大佬多多指教!!!
加载全部内容