一、引言
在日常生活中,我们经常需要管理大量的联系人信息,如姓名、性别、年龄、电话和地址等。为了方便地管理和查询这些信息,我们可以使用C语言编写一个通讯录管理系统。本文将带领大家一步步实现这个系统。
二、需求分析
1.项目概述
通讯录管理系统是一款用于存储、管理和操作联系人信息的程序。该系统允许用户添加、删除、修改、查找和展示联系人,并将数据持久化到文件中,以便在程序重新启动时可以恢复数据。
2.功能需求
添加联系人:允许用户添加新的联系人信息
删除联系人:根据用户输入的联系人姓名,删除对应的联系人信息。
修改联系人:允许用户修改已添加的联系人信息。
查询联系人:根据用户输入的联系人姓名,查询对应的联系人信息。
显示联系人:显示所有已添加的联系人信息。
加载数据:在程序启动时,系统需要从指定的文件中读取联系人数据,并恢复通讯录的状态。
保存数据: 在程序关闭前,系统需要将当前通讯录数据持久化到文件中,以便在下次启动程序时可以恢复数据。
3.数据结构设计
为了存储联系人信息,我们定义一个结构体如下:
typedef struct personInfo { char name[NAME_MAX];//姓名 char gender[GENDER_MAX];//性别 int age;//年龄 char tel[TEL_MAX];//电话号码 char address[ADDRESS_MAX];//地址 }Per;
三、功能设计
1.初始化通讯录
初始化通讯录管理系统,准备接收用户操作。
//初始化 void ContactInit(Con* p) { p->arr = NULL; p->size = p->capacity = 0; }
2. 添加联系人
提示用户输入联系人的姓名、性别、年龄、电话和地址。
提示用户添加成功。
//检查容量 void checkcapacity(Con* p) { if (p->size == p->capacity) { int newcapacity = p->capacity == 0 ? 4 : 2 * p->capacity; DataType* tmp = (DataType*)realloc(p->arr, newcapacity * sizeof(DataType)); if (tmp == NULL) { perror("realloc fail"); exit(1); } //空间申请成功 p->arr = tmp; p->capacity = newcapacity; } } //顺序表尾插 void SLPushBack(Con* p, DataType x) { assert(p); checkcapacity(p); p->arr[p->size++] = x; } //添加数据 void ContactAdd(Con* p) { Per info; printf("请输入要添加的联系人姓名:\n"); scanf("%s", info.name); printf("请输入要添加的联系人性别:\n"); scanf("%s", info.gender); printf("请输入要添加的联系人年龄:\n"); scanf("%d", &info.age); printf("请输入要添加的联系人电话:\n"); scanf("%s", info.tel); printf("请输入要添加的联系人地址:\n"); scanf("%s", info.address); SLPushBack(p, info); printf("添加成功!\n"); }
3. 删除联系人
提示用户输入要删除联系人的姓名。
在通讯录文件中查找对应的联系人。
如果找到,删除该联系人的信息,并提示用户删除成功。
如果未找到,提示用户联系人不存在。
//查找数据 int FindByName(Con* p, char name[]) { for (int i = 0; i < p->size; i++) { if (strcmp(p->arr[i].name, name) == 0) return i;//找到了 } return -1;//没有找到 } //删除数据 void ContactDel(Con* p) { //前提是数据必须存在才能删除---查找数据是否存在 char name[NAME_MAX]; printf("请输入要删除的联系人姓名:\n"); scanf("%s", name); int find = FindByName(p, name); if (find < 0) { printf("要删除的联系人不存在!\n"); return; } SLErase(p, find); printf("删除成功!\n"); }
4. 修改联系人
提示用户输入要修改联系人的姓名。
在通讯录文件中查找对应的联系人。
如果找到,允许用户修改联系人的信息,并提示用户修改成功。
如果未找到,提示用户联系人不存在。
//查找数据 /* int FindByName(Con* p, char name[]) { for (int i = 0; i < p->size; i++) { if (strcmp(p->arr[i].name, name) == 0) return i;//找到了 } return -1;//没有找到 } */ //通讯录的修改 void ContactMod(Con* p) { //前提是数据必须存在才能修改---查找数据是否存在 char name[NAME_MAX]; printf("请输入要修改的联系人姓名:\n"); scanf("%s", name); int find = FindByName(p, name); if (find < 0) { printf("要修改的联系人不存在!\n"); return; } //直接修改 printf("请输入修改后的姓名:\n"); scanf("%s", p->arr[find].name); printf("请输入修改后的性别:\n"); scanf("%s", p->arr[find].gender); printf("请输入修改后的年龄:\n"); scanf("%d", &p->arr[find].age); printf("请输入修改后的电话:\n"); scanf("%s", p->arr[find].tel); printf("请输入修改后的地址:\n"); scanf("%s", p->arr[find].address); printf("修改成功!\n"); }
5. 查询联系人
提示用户输入要查询联系人的姓名。
在通讯录中查找对应的联系人。
如果找到,显示该联系人的详细信息。
如果未找到,提示用户联系人不存在。
//查找数据 /* int FindByName(Con* p, char name[]) { for (int i = 0; i < p->size; i++) { if (strcmp(p->arr[i].name, name) == 0) return i;//找到了 } return -1;//没有找到 } */ void ContactFind(Con* p) { char name[NAME_MAX]; printf("请输入要查找的联系人姓名:\n"); scanf("%s", name); int find = FindByName(p, name); if (find < 0) { printf("要查找的联系人不存在!\n"); return; } printf("%s\t%s\t%s\t%s\t\t%s\t\n", "姓名", "性别", "年龄", "电话", "地址"); printf("%s\t%s\t%d\t%s\t%s\t\n", p->arr[find].name, p->arr[find].gender, p->arr[find].age, p->arr[find].tel, p->arr[find].address ); }
6. 展示所有联系人
读取通讯录中的所有联系人信息。
在屏幕上以表格形式展示所有联系人信息。
//展示通讯录 void ContactShow(Con* p) { //表头:姓名 性别 年龄 电话 地址 printf("%s\t%s\t%s\t%s\t\t%s\t\n", "姓名", "性别", "年龄", "电话", "地址"); for (int i = 0; i < p->size; i++) { printf("%s\t%s\t%d\t%s\t%s\t\n", p->arr[i].name, p->arr[i].gender, p->arr[i].age, p->arr[i].tel, p->arr[i].address ); } }
7. 保存文件
在程序退出前,保存所有更改到通讯录文件。
//将数据保存到文件中 void SaveContact(Con* p) { FILE* pf = fopen("contact.txt", "wb"); if (pf == NULL) { perror("fopen error!\n"); return; } //将通讯录数据写入文件 for (int i = 0; i < p->size; i++) { fwrite(p->arr + i, sizeof(Per), 1, pf); } printf("通讯录数据保存成功!\n"); }
8. 加载数据
程序启动时,自动加载通讯录文件中的历史数据。
如果文件不存在或为空,则初始化一个空的通讯录。
//从文件读取历史数据 void LoadContact(Con* p) { FILE* pf = fopen("contact.txt", "rb"); if (pf == NULL) { perror("fopen error!\n"); return; } //循环读取文件数据 Per info; printf("正在导入历史数据...\n"); Sleep(1000); while (fread(&info, sizeof(Per), 1, pf)) { SLPushBack(p, info); } printf("历史数据导入通讯录成功!\n"); system("pause"); system("cls"); }
四、完整代码
1.Contact.h
#pragma once #define NAME_MAX 20 #define GENDER_MAX 10 #define TEL_MAX 20 #define ADDRESS_MAX 100 #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<windows.h> //定义结构体存储个人信息(自定义类型) typedef struct personInfo { char name[NAME_MAX]; char gender[GENDER_MAX]; int age; char tel[TEL_MAX]; char address[ADDRESS_MAX]; }Per; typedef Per DataType;//这里类型为自定义类型Per //通讯录:底层为顺序表 typedef struct Contact { DataType* arr; int size;//有效数据个数 int capacity;//容量 }Con; //初始化通讯录 void ContactInit(Con* p); //销毁通讯录 void ContactDestory(Con* p); //展示通讯录 void ContactShow(Con* p); //添加数据 void ContactAdd(Con* p); //删除数据 void ContactDel(Con* p); //通讯录的修改 void ContactMod(Con* p); //通讯录的查找 void ContactFind(Con* p); //从文件读取历史数据 void LoadContact(Con* p); //将数据保存到文件中 void SaveContact(Con* p);
2.Contact.c
#define _CRT_SECURE_NO_WARNINGS #include"Contact.h" //初始化 void ContactInit(Con* p) { p->arr = NULL; p->size = p->capacity = 0; } //销毁 void ContactDestory(Con* p) { if (p->arr) { free(p->arr); } p->arr = NULL; p->size = p->capacity = 0; } //检查容量 void checkcapacity(Con* p) { if (p->size == p->capacity) { int newcapacity = p->capacity == 0 ? 4 : 2 * p->capacity; DataType* tmp = (DataType*)realloc(p->arr, newcapacity * sizeof(DataType)); if (tmp == NULL) { perror("realloc fail"); exit(1); } //空间申请成功 p->arr = tmp; p->capacity = newcapacity; } } //顺序表尾插 void SLPushBack(Con* p, DataType x) { assert(p); checkcapacity(p); p->arr[p->size++] = x; } //删除指定位置的数据 void SLErase(Con* p, int pos) { assert(p); assert(pos >= 0 && pos < p->size); for (int i = pos; i <= p->size - 2; i++) { p->arr[i] = p->arr[i + 1]; } p->size--; } //添加数据 void ContactAdd(Con* p) { Per info; printf("请输入要添加的联系人姓名:\n"); scanf("%s", info.name); printf("请输入要添加的联系人性别:\n"); scanf("%s", info.gender); printf("请输入要添加的联系人年龄:\n"); scanf("%d", &info.age); printf("请输入要添加的联系人电话:\n"); scanf("%s", info.tel); printf("请输入要添加的联系人地址:\n"); scanf("%s", info.address); SLPushBack(p, info); printf("添加成功!\n"); } //查找数据 int FindByName(Con* p, char name[]) { for (int i = 0; i < p->size; i++) { if (strcmp(p->arr[i].name, name) == 0) return i;//找到了 } return -1;//没有找到 } //删除数据 void ContactDel(Con* p) { //前提是数据必须存在才能删除---查找数据是否存在 char name[NAME_MAX]; printf("请输入要删除的联系人姓名:\n"); scanf("%s", name); int find = FindByName(p, name); if (find < 0) { printf("要删除的联系人不存在!\n"); return; } SLErase(p, find); printf("删除成功!\n"); } //展示通讯录 void ContactShow(Con* p) { //表头:姓名 性别 年龄 电话 地址 printf("%s\t%s\t%s\t%s\t\t%s\t\n", "姓名", "性别", "年龄", "电话", "地址"); for (int i = 0; i < p->size; i++) { printf("%s\t%s\t%d\t%s\t%s\t\n", p->arr[i].name, p->arr[i].gender, p->arr[i].age, p->arr[i].tel, p->arr[i].address ); } } //通讯录的修改 void ContactMod(Con* p) { //前提是数据必须存在才能修改---查找数据是否存在 char name[NAME_MAX]; printf("请输入要修改的联系人姓名:\n"); scanf("%s", name); int find = FindByName(p, name); if (find < 0) { printf("要修改的联系人不存在!\n"); return; } //直接修改 printf("请输入修改后的姓名:\n"); scanf("%s", p->arr[find].name); printf("请输入修改后的性别:\n"); scanf("%s", p->arr[find].gender); printf("请输入修改后的年龄:\n"); scanf("%d", &p->arr[find].age); printf("请输入修改后的电话:\n"); scanf("%s", p->arr[find].tel); printf("请输入修改后的地址:\n"); scanf("%s", p->arr[find].address); printf("修改成功!\n"); } //通讯录的查找 void ContactFind(Con* p) { char name[NAME_MAX]; printf("请输入要查找的联系人姓名:\n"); scanf("%s", name); int find = FindByName(p, name); if (find < 0) { printf("要查找的联系人不存在!\n"); return; } printf("%s\t%s\t%s\t%s\t\t%s\t\n", "姓名", "性别", "年龄", "电话", "地址"); printf("%s\t%s\t%d\t%s\t%s\t\n", p->arr[find].name, p->arr[find].gender, p->arr[find].age, p->arr[find].tel, p->arr[find].address ); } //从文件读取历史数据 void LoadContact(Con* p) { FILE* pf = fopen("contact.txt", "rb"); if (pf == NULL) { perror("fopen error!\n"); return; } //循环读取文件数据 Per info; printf("正在导入历史数据...\n"); Sleep(1000); while (fread(&info, sizeof(Per), 1, pf)) { SLPushBack(p, info); } printf("历史数据导入通讯录成功!\n"); system("pause"); system("cls"); } //将数据保存到文件中 void SaveContact(Con* p) { FILE* pf = fopen("contact.txt", "wb"); if (pf == NULL) { perror("fopen error!\n"); return; } //将通讯录数据写入文件 for (int i = 0; i < p->size; i++) { fwrite(p->arr + i, sizeof(Per), 1, pf); } printf("通讯录数据保存成功!\n"); }
3.main.c
#define _CRT_SECURE_NO_WARNINGS #include"Contact.h" void menu() { printf("***************通讯录***************\n"); printf("****1.添加联系人 2.删除联系人****\n"); printf("****3.修改联系人 4.查找联系人****\n"); printf("****5.展示联系人 0.退出并保存****\n"); printf("************************************\n"); } int main() { Con con;//创建 ContactInit(&con);//初始化 LoadContact(&con);//导入历史数据 int option = -1; int(*pfunc[6])(Con * p) = { 0,ContactAdd,ContactDel,ContactMod,ContactFind,ContactShow }; //转移表 do { menu(); printf("请选择你的操作:\n"); scanf("%d", &option); //转移表开始 if (option >= 1 && option <= 6)//根据对应的option执行不同操作 { (*pfunc[option])(&con); } else if (option == 0) { printf("退出通讯录!\n"); } else { printf("输入有误,请重新选择!\n"); } system("pause"); system("cls");//转移表末尾 //我用的是转移表写,也可以正常使用switch-case语句,使用switch语句就把转移表那段注释掉 /*switch (option) { case 1: ContactAdd(&con); system("pause"); system("cls"); break; case 2: ContactDel(&con); system("pause"); system("cls"); break; case 3: ContactMod(&con); system("pause"); system("cls"); break; case 4: ContactFind(&con); system("pause"); system("cls"); break; case 5: ContactShow(&con); system("pause"); system("cls"); break; case 0: printf("退出通讯录!\n"); break; default: printf("输入有误,请重新选择!\n"); system("pause"); system("cls"); break; }*/ } while (option != 0); SaveContact(&con);//保存数据到文件 ContactDestory(&con);//销毁 return 0; }
五、测试运行
菜单界面
添加联系人
展示联系人
六、总结
本文通过一个简单的通讯录管理系统,介绍了C语言在文件操作、结构体、函数等方面的应用。通过学习本文,读者可以掌握C语言编程的基本技巧,为以后的项目开发奠定基础。当然,这个通讯录管理系统还有许多可以优化的地方,比如增加联系人信息的排序、分页显示等功能,感兴趣的读者可以继续完善。