✨✨ 欢送大家来到贝蒂大讲堂✨✨
养成好习惯,先赞后看哦~
所属专栏:C语言我的项目
贝蒂的主页:Betty‘s blog
引言:
在咱们大抵学习完C语言之后,咱们就能够利用目前所学的常识去做一些有意思的我的项目,而明天贝蒂就带大家实现一个通讯录的繁难实现,
本章你可能须要的常识:
- 动态内存的应用:动态内存治理
- 文件的创立与应用:[文件操作]()
1. 通讯录要求
- 通讯录包含每个人的姓名,性别,年龄,电话与地址。
- 玩家能够自由选择通讯录的进出。
- 玩家能够自在增删查改通讯录中的数据。
2. 多文件治理
为了不便代码的治理和保障通讯录实现逻辑的清晰性,咱们将采纳多文件治理的模式。
- 创立头文件contact.c,蕴含所有头文件(其余源文件只需援用它即可),以及所有通讯录性能的展示。
- 创立源文件contact.c,负责通讯录所有性能的具体代码实现。
- 创立源文件text.c,负责展示通讯录实现的总体逻辑。
3. 通讯录的筹备
3.1 预处理信息
为了不便咱们后续更换通信的信息,咱们能够利用宏来定义通讯录的具体信息的大小。
#define MAX 100//最大人数#define MAX_NAME 20//名字最大长度#define MAX_SEX 5//性别最大长度#define MAX_TELE 12//电话最大长度#define MAX_ADDR 30//地址最大长度
3.2 构造体定义
每个人的通讯录都要蕴含姓名,性别,年龄,电话与地址等信息,这时就须要咱们创立一个构造体来方便管理。
typedef struct PeoInfo{ char name[MAX_NAME];//名字 int age;//年龄 char sex[MAX_SEX];//性别 char tele[MAX_TELE];//电话 char addr[MAX_ADDR];//地址}PeoInfo;
而咱们须要用这个构造体创立一个大小为100的数组,并且咱们还须要晓得以后通讯录的大小能力进行增删查改的操作,这两者非亲非故,为了简化代码和减少代码的可读性,咱们能够将这两者从新定义一个构造体。
typedef struct contact{ PeoInfo data[MAX];//一百个人的数据 int sz;//通讯录的大小}contact;
4. 繁难菜单
void menu(){ printf("***********************************\n"); printf("** 1.add 2.delete **\n"); printf("** 3.search 4.modify **\n"); printf("** 5.display 6.sort **\n"); printf("** 0.exit **\n"); printf("***********************************\n");}
画面展现:
5. 通讯录具体性能
5.1 初始化
咱们首先对通讯录进行初始化。
void InitContact(contact* pc)//初始化{ assert(pc); pc->sz = 0; memset(pc->data, 0, sizeof(pc->data));}
5.2 展现联系人
当用户抉择5时主动展现通讯录中的用户,并且展现用户过程中须要进行对齐,便于用户观看。
void DisplayContact(contact* pc)//打印信息{ assert(pc); printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪", "性别", "电话", "地址");//默认右对齐,批改为左对齐 //两头也要留下足够的空间 for (int i = 0; i < pc->sz; i++) { printf("%-15s\t%-5d\t%-5s\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.3 增加联系人
用户抉择1能够自在增加联系人,如果通讯录已满,则揭示用户通讯录已满,请先清理通讯录。
(1) 查看通讯录是否已满
如果通讯录满了返回0,未满则返回1。
int CheckContact(contact*pc)//查看大小{ assert(pc); if (pc->sz == 100) { return 0; } return 1;}
(2) 增加
当通讯录未满时,用户能够输出数据增加新用户。
void AddContact(contact* pc)//减少联系人{ assert(pc); int ret = CheckContact(pc);//查看是否满了 if (ret == 0) { printf("通讯录已满,请先清理通讯录!!\n"); return; } printf("请输出联系人的姓名:> "); scanf("%s", pc->data[pc->sz].name); printf("请输出联系人的年龄:> "); scanf("%d", &(pc->data[pc->sz].age)); printf("请输出联系人的性别:> "); scanf("%s", pc->data[pc->sz].sex); printf("请输出联系人的电话:> "); scanf("%s", pc->data[pc->sz].tele); printf("请输出联系人的地址:> "); scanf("%s", pc->data[pc->sz].addr); printf("用户增加胜利!\n"); pc->sz++;}
5.4 删除联系人
用户能够抉择2清理通讯录,删除指定联系人。
(1) 寻找下标
在删除指定练习人时咱们需通过其姓名寻找该联系人的下标。找到返回其下标,否则返回-1、
int FindName(contact* pc, char name[]){ assert(pc&&name); for (int pos = 0; pos < pc->sz; pos++) { if (strcmp(pc->data[pos].name, name) == 0) { return pos; } } return -1;}
(2) 删除
通过寻找到的下标,咱们能够利用前面的数据顺次笼罩来达到删除的目标。
留神:咱们不能笼罩最初一个数据否则就会产生数组越界,这时咱们只需减去通讯录此时的大小就好了。
void DeleteContact(contact* pc)//删除联系人{ assert(pc); assert(pc->sz >= 0); char name[MAX_NAME]; printf("请输出须要删除人的姓名:> "); scanf("%s", name); int pos = FindName(pc, name); if (pos == -1) { printf("通讯录中并没有这个人!!\n"); return; } for (int i = pos; i < pc->sz - 1; i++) { pc->data[i] = pc->data[i + 1];//笼罩 } printf("删除胜利\n"); pc->sz--;}
删除前:
删除后 :
5.5 查找联系人
通过抉择3查找指定联系人,没有找到则揭示用户没有该用户,查找到就打印其信息。
void SearchContact(contact* pc)//查找联系人{ assert(pc); char name[MAX_NAME]; printf("请输出须要查找人的姓名:> "); scanf("%s", name); int pos = FindName(pc, name); if (pos == -1) { printf("通讯录中并没有这个人!!\n"); return; } printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪", "性别", "电话", "地址"); printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);}
5.6 批改联系人
咱们能够通过抉择4批改指定联系人的信息。
void ModifyContact(contact* pc)//批改联系人{ assert(pc); char name[MAX_NAME]; printf("请输出须要批改人的姓名:> "); scanf("%s", name); int pos = FindName(pc, name); if (pos == -1) { printf("通讯录中并没有这个人!!\n"); return; } printf("请输出联系人的姓名:> "); scanf("%s", pc->data[pos].name); printf("请输出联系人的年龄:> "); scanf("%d", &(pc->data[pos].age)); printf("请输出联系人的性别:> "); scanf("%s", pc->data[pos].sex); printf("请输出联系人的电话:> "); scanf("%s", pc->data[pos].tele); printf("请输出联系人的地址:> "); scanf("%s", pc->data[pos].addr); printf("批改胜利\n");}
批改前:
批改后:
5.7 排序联系人
咱们能够抉择6对通讯录进行排序,能够依照姓名,年纪,性别,电话,地址排序。
int cmp1(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);}int cmp2(const void* p1, const void* p2){ return ((PeoInfo*)p1)->age- ((PeoInfo*)p2)->age;}int cmp3(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->sex, ((PeoInfo*)p2)->sex);}int cmp4(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->tele, ((PeoInfo*)p2)->tele);}int cmp5(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->addr, ((PeoInfo*)p2)->addr);}void SortByName(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp1);}void SortByAge(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp2);}void SortBySex(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp3);}void SortByTele(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);}void SortByAddr(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);}void SortContact(contact* pc)//排序联系人{ assert(pc); printf("请抉择如何排序:> "); char input[20]; scanf("%s", input); if (strcmp(input, "姓名")==0) { SortByName(pc);//按姓名排序 } else if (strcmp(input, "年龄") == 0) { SortByAge(pc);//按年龄排序 } else if (strcmp(input, "性别") == 0) { SortBySex(pc);//按性别排序 } else if (strcmp(input, "电话") == 0) { SortByTele(pc);//按电话排序 } else if (strcmp(input, "地址") == 0) { SortByAddr(pc);//按地址排序 } else { printf("输出非法,请从新输出\n"); }}
6. 改良通讯录
6.1 动态内存开拓
上述通讯录有一个致命的毛病——通讯录大小固定,为了解决这个问题我能够应用咱们后面学过的动态内存开拓。
(1) 新增变量
为了不便咱们晓得此时的容量大小,咱们将在构造体中退出新的变量。
typedef struct contact{ PeoInfo *data;//动静开拓的数据 int sz;//通讯录的大小 int capacity;//通讯录的容量}contact;
(2) 初始化
void InitContact(contact* pc)//初始化{ assert(pc); pc->sz = 0; pc->data = (PeoInfo*)calloc(2, sizeof(PeoInfo)); if (pc->data == NULL) { perror("InitContact:"); return; } pc->capacity = 2; /*memset(pc->data, 0, sizeof(pc->data));*/}
(3) 增容
当通讯录用户数量满时减少内存空间。
void CheckCapacity(contact* pc)//增容{ if (pc->sz == pc->capacity) { PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo)); if (tmp != NULL) { pc->data = tmp; } else { perror("CheckCapacity:"); return; } pc->capacity += 2; printf("增容胜利\n"); }}
6.2 文件保留
咱们发现每当咱们关闭程序时咱们写入的数据会被清空,咱们要想保留上次写入的数据就应该应用文件操作。
(1) 加载数据
每次初始化时就要将上次文件的数据导入进来。
void LoadContact(contact* pc)//加载上次数据{ PeoInfo tmp = { 0 }; FILE* pf = fopen("contact.txt", "rb"); if (pf == NULL) { return; } //读取文件,寄存到通讯录 while (fread(&tmp, sizeof(PeoInfo), 1, pf)) //返回值为0就是遇见文件开端 { CheckCapacity(pc); pc->data[pc->sz] = tmp; pc->sz++; } fclose(pf); pf = NULL;}
(2) 保留数据
当程序失常完结时保留数据。
void SaveContact(contact* pc)//保留数据{ FILE* pfWrite = fopen("contact.txt", "wb"); if (pfWrite == NULL) { perror("fopen:"); return; } //写通讯录中数据到文件中 int i = 0; for (i = 0; i < pc->sz; i++) { fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite); } fclose(pfWrite); pfWrite = NULL; printf("保留胜利\n");}
7. 通讯录逻辑的搭建
- 用户通过数字抉择性能。
- 每次抉择完之后返回菜单。
- 抉择退出之后保留数据,程序完结。
enum Option //利用枚举减少代码的可读性{ EXIT,//0 ADD, DEL, SEARCH, MODIFY, DISPLAY, SORT};void test(){ int input = 0; contact con; InitContact(&con); do { menu(); printf("请输出你的抉择:>"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DEL: DeleteContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyContact(&con); break; case DISPLAY: DisplayContact(&con); break; case SORT: SortContact(&con); break; case EXIT: SaveContact(&con);//退出保留数据 DestroyContact(&con); break; default: printf("非法输出,请从新输出\n"); break; } } while (input);}int main(){ test(); return 0;}
8. 残缺代码
8.1 contact.h
#include<stdio.h>#include<string.h>#include<stdlib.h>#include<assert.h>#define MAX_NAME 20//名字最大长度#define MAX_SEX 5//性别最大长度#define MAX_TELE 12//电话最大长度#define MAX_ADDR 30//地址最大长度typedef struct PeoInfo{ char name[MAX_NAME];//名字 int age;//年龄 char sex[MAX_SEX];//性别 char tele[MAX_TELE];//电话 char addr[MAX_ADDR];//地址}PeoInfo;typedef struct contact{ PeoInfo *data;//动静开拓的数据 int sz;//通讯录的大小 int capacity;//通讯录的容量}contact;void InitContact(contact* pc);//初始化void DisplayContact(contact* pc);//打印信息void AddContact(contact* pc);//减少联系人void DeleteContact(contact* pc);//删除联系人void SearchContact(contact* pc);//查找联系人void ModifyContact(contact* pc);//批改联系人void SortContact(contact* pc);//排序联系人void SaveContact(contact* pc);//保留数据void DestroyContact(contact* pc);//销毁内存
8.2 contact.c
#include"contact.h"void CheckCapacity(contact* pc)//增容{ if (pc->sz == pc->capacity) { PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo)); if (tmp != NULL) { pc->data = tmp; } else { perror("CheckCapacity:"); return; } pc->capacity += 2; printf("增容胜利\n"); }}void LoadContact(contact* pc)//加载上次数据{ PeoInfo tmp = { 0 }; FILE* pf = fopen("contact.txt", "rb"); if (pf == NULL) { return; } //读取文件,寄存到通讯录 while (fread(&tmp, sizeof(PeoInfo), 1, pf)) //返回值为0就是遇见文件开端 { CheckCapacity(pc); pc->data[pc->sz] = tmp; pc->sz++; } fclose(pf); pf = NULL;}void InitContact(contact* pc)//初始化{ assert(pc); pc->sz = 0; pc->data = (PeoInfo*)calloc(2, sizeof(PeoInfo)); if (pc->data == NULL) { perror("InitContact:"); return; } pc->capacity = 2; LoadContact(pc); /*memset(pc->data, 0, sizeof(pc->data));*/}void DisplayContact(contact* pc)//打印信息{ assert(pc); printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪", "性别", "电话", "地址");//默认右对齐,批改为左对齐 //两头也要留下足够的空间 for (int i = 0; i < pc->sz; i++) { printf("%-15s\t%-5d\t%-5s\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); }}void AddContact(contact* pc)//减少联系人{ assert(pc); CheckCapacity(pc);//查看是否满了 printf("请输出联系人的姓名:> "); scanf("%s", pc->data[pc->sz].name); printf("请输出联系人的年龄:> "); scanf("%d", &(pc->data[pc->sz].age)); printf("请输出联系人的性别:> "); scanf("%s", pc->data[pc->sz].sex); printf("请输出联系人的电话:> "); scanf("%s", pc->data[pc->sz].tele); printf("请输出联系人的地址:> "); scanf("%s", pc->data[pc->sz].addr); printf("用户增加胜利!\n"); pc->sz++;}int FindName(contact* pc, char name[]){ assert(pc&&name); for (int pos = 0; pos < pc->sz; pos++) { if (strcmp(pc->data[pos].name, name) == 0) { return pos; } } return -1;}void DeleteContact(contact* pc)//删除联系人{ assert(pc); assert(pc->sz >= 0); char name[MAX_NAME]; printf("请输出须要删除人的姓名:> "); scanf("%s", name); int pos = FindName(pc, name); if (pos == -1) { printf("通讯录中并没有这个人!!\n"); return; } for (int i = pos; i < pc->sz - 1; i++) { pc->data[i] = pc->data[i + 1];//笼罩 } printf("删除胜利\n"); pc->sz--;}void SearchContact(contact* pc)//查找联系人{ assert(pc); char name[MAX_NAME]; printf("请输出须要查找人的姓名:> "); scanf("%s", name); int pos = FindName(pc, name); if (pos == -1) { printf("通讯录中并没有这个人!!\n"); return; } printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪", "性别", "电话", "地址"); printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);}void ModifyContact(contact* pc)//批改联系人{ assert(pc); char name[MAX_NAME]; printf("请输出须要批改人的姓名:> "); scanf("%s", name); int pos = FindName(pc, name); if (pos == -1) { printf("通讯录中并没有这个人!!\n"); return; } printf("请输出联系人的姓名:> "); scanf("%s", pc->data[pos].name); printf("请输出联系人的年龄:> "); scanf("%d", &(pc->data[pos].age)); printf("请输出联系人的性别:> "); scanf("%s", pc->data[pos].sex); printf("请输出联系人的电话:> "); scanf("%s", pc->data[pos].tele); printf("请输出联系人的地址:> "); scanf("%s", pc->data[pos].addr); printf("批改胜利\n");}int cmp1(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);}int cmp2(const void* p1, const void* p2){ return ((PeoInfo*)p1)->age- ((PeoInfo*)p2)->age;}int cmp3(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->sex, ((PeoInfo*)p2)->sex);}int cmp4(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->tele, ((PeoInfo*)p2)->tele);}int cmp5(const void* p1, const void* p2){ return strcmp(((PeoInfo*)p1)->addr, ((PeoInfo*)p2)->addr);}void SortByName(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp1);}void SortByAge(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp2);}void SortBySex(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp3);}void SortByTele(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);}void SortByAddr(contact* pc){ qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);}void SortContact(contact* pc)//排序联系人{ assert(pc); printf("请抉择如何排序:> "); char input[20] = { 0 }; scanf("%s", input); if (strcmp(input, "姓名")==0) { SortByName(pc);//按姓名排序 } else if (strcmp(input, "年龄") == 0) { SortByAge(pc);//按年龄排序 } else if (strcmp(input, "性别") == 0) { SortBySex(pc);//按性别排序 } else if (strcmp(input, "电话") == 0) { SortByTele(pc);//按电话排序 } else if (strcmp(input, "地址") == 0) { SortByAddr(pc);//按地址排序 } else { printf("输出非法,请从新输出\n"); }}void DestroyContact(contact* pc){ free(pc->data); pc->data= NULL; pc->capacity = 0; pc->sz = 0; printf("销毁胜利\n");}void SaveContact(contact* pc)//保留数据{ FILE* pfWrite = fopen("contact.txt", "wb"); if (pfWrite == NULL) { perror("fopen:"); return; } //写通讯录中数据到文件中 int i = 0; for (i = 0; i < pc->sz; i++) { fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite); } fclose(pfWrite); pfWrite = NULL; printf("保留胜利\n");}
8.3 text.c
#include"contact.h"enum Option //利用枚举减少代码的可读性{ EXIT,//0 ADD, DEL, SEARCH, MODIFY, DISPLAY, SORT};void menu(){ printf("***********************************\n"); printf("** 1.add 2.delete **\n"); printf("** 3.search 4.modify **\n"); printf("** 5.display 6.sort **\n"); printf("** 0.exit **\n"); printf("***********************************\n");}void test(){ int input = 0; contact con; InitContact(&con); do { menu(); printf("请输出你的抉择:>"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DEL: DeleteContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyContact(&con); break; case DISPLAY: DisplayContact(&con); break; case SORT: SortContact(&con); break; case EXIT: SaveContact(&con);//退出保留数据 DestroyContact(&con); break; default: printf("非法输出,请从新输出\n"); break; } } while (input);}int main(){ test(); return 0;}