用C语言和文本文件实现一个简单的,可保存的通讯录
我们先思考一个通讯录都有那些信息很明显通讯录记录的是人人有哪些信息呢这里我就写5个吧分别是姓名年龄电话性别地址然后我们把他们写成一个结构体最好定义在头文件里这样在使用的时候更方便我们还可以把要使用的一些常用的或者要修改的常量定义成枚举,后续有什么要添加的直接就能在枚举中添加//枚举的一些关键常量 enum NUM { NAME 20, //名字 TELE 12, //电话 SEX 10, //性别 ADDR 100, //地址 ADD 1,DEL 2,SEARCH 3,MODIFY 4,SHOW 5,SORT 6,EXIT 0,CLEAR 7,//菜单 //ARRDATA 100 SIZE 3, //通讯录初始容量大小 SIZEADD 2 //每次扩容的增加量 }; //人的信息 typedef struct peo { char name[NAME]; //姓名 int age; //年龄 char tele[TELE]; //电话 char sex[SEX]; //性别 char addr[ADDR]; //地址 }peo;这里的枚举内容我直接把参数都写里了后续在思考的过程就不再改动了接下来思考光有一个人的信息是不够的无法完整表达一个通讯录当然由于我们只是简单实现我们就只再记录通讯录的人员数量和通讯录的容量把他们简单封装成一个结构体typedef struct contact { peo* data; //人的相关数据 size_t count; //成员数量 size_t capacity; //通讯录容量 }contact;这里我都是进行了重命名了的方便后续的使用这些都是头文件里面要用的还有一些库函数我就在这里提前定义了#includestdio.h #includestring.h #includeassert.h #includestdlib.h #includeerrno.h然后我们写主函数我们考虑通讯录运行一定要有菜单然后我们用switch语句写分支结构我们需要用户主动输入选择所以我们还需要一个变量接收这个变量还可以用于switch语句的判断菜单我们可以单独封装一个函数可以实现增加联系人删除联系人查找联系人修改联系人显示联系人按名字排序联系人清空联系人和退出保存将数据保存到文件中把这些功能都封装成函数还得创建一个结构体变量用于存放通讯录的数据还需要一个函数对结构体进行初始化并把保存在文件中的信息放入到结构体中于是主函数如下#includecontact.h static void menu() //菜单函数 { printf(****************************************\n); printf(****************************************\n); printf(****************************************\n); printf(*** 1.add 2.del ******\n); printf(*** 3.search 4.modify ******\n); printf(*** 5.show 6.sort ******\n); printf(*** 0.exit 7.clear ******\n); printf(****************************************\n); printf(****************************************\n); printf(****************************************\n); } int main() { int input 0; //存放菜单选择输入的值 contact con; //创建存放通讯录数据的结构体 init_contact(con);//初始化结构体 do { menu(); printf(请输入要进行的功能\n); scanf(%d, input); //菜单的选择输入 switch (input) //菜单选择的判断逻辑 { case ADD: add_contact(con); //添加函数 break; case DEL: del_contact(con); //删除函数 break; case SEARCH: search_contact(con); //查找函数 break; case MODIFY: modify_contact(con); //修改函数 break; case SHOW: show_contact(con); //显示函数 break; case SORT: sort_contact(con); //排序函数 break; case CLEAR: clear_contact(con); //清空函数 break; case EXIT: save_contact(con); //保存函数 destroycontact(con); //推出后内存的销毁 printf(程序退出\n); break; default: printf(选择错误请重新选择\n);//其他逻辑判断 break; } } while (input)//do while的循环判断 return 0; }主函数别忘了引用头文件接下来我们封装函数把函数单独放在一个源文件#includecontact.h //初始化函数 //对于初始化我们不仅要初始化结构体内容还要把之前保存在文件中的信息放入到结构体中当然还涉及到一个动态扩容的问题我们的结构体初始设计的是有容量的如果里面放的信息多了还要涉及到扩容的问题 这些都要设计函数 static void expandCapacity(contact* con) //扩容 { assert(con); //断言防止空指针 if (con-capacity con-count) //容量判断 { void* p 0; p realloc(con-data, (con-capacity SIZEADD) * sizeof(peo));//追加信息容量 if (NULL p) //报错判断 { printf(expandCapacity::%s\n, strerror(errno)); return; } con-capacity SIZEADD;//修改容量标记 con-data (peo*)p; //修改指针位置其实没变 printf(增容成功\n); } } static int read_conact(contact* con) //读取文件中保存的通讯录信息 { static void expandCapacity(contact * con); //函数声明 FILE* p fopen(contact.txt, r); //打开文件 if (p NULL) //报错判断 { return 1; } peo a { 0 }; while (fread(a, sizeof(peo), 1, p) 1)//循环提取信息这里取决于保存函数的写法 { expandCapacity(con); //扩容 con-data[con-count] a;//赋值 (con-count);//成员计数加一 } fclose(p); p NULL;//关闭文件指针空置 return 0; } void init_contact(contact* con) //初始化函数 { static int read_conact(contact * con);//函数调用声明 assert(con);////断言防止空指针 con-count 0;//初始化人数 void* p 0;//内存指针 p calloc(SIZE, sizeof(peo));//创建内存 if (NULL con-data)//报错判断 { printf(init_contact:calloc:%s\n, strerror(errno)); return; } con-data (peo*)p;//初始化人信息指针 con-capacity SIZE;//初始化容量 if (read_conact(con) 1)//读取之前的数据 { perror(读取旧数据失败);//问题判断 return; } } //初始化我们已经完成了 //接下来我们写添加函数 void add_contact(contact* con) { static void expandCapacity(contact * con); //函数声明 assert(con);//断言防止空指针 expandCapacity(con);//扩容判断 printf(请输入姓名\n); scanf(%s, (con-datacon-count)-name); printf(请输入年龄\n); scanf(%d, ((con-data con-count)-age)); printf(请输入电话\n); scanf(%s, con-data[con-count].tele); printf(请输入性别\n); scanf(%s, con-data[con-count].sex); printf(请输入地址\n); scanf(%s, con-data[con-count].addr);//这些都是信息是输入 con-count;//计数加一 printf(增加成功\n); } //接下来我们写显示函数 void show_contact(contact* con) { assert(con);//断言防止空指针 int i 0; printf(%-20s\t%-5s\t%5s\t%12s\t%30s\n, 姓名, 年龄, 电话, 性别, 地址); //显示时方便观察打印一个标签 for (i 0; i con-count; i)//循环打印人员信息 { printf(%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n, con-data[i].name, con-data[i].age, con-data[i].tele, con-data[i].sex, con-data[i].addr); } } //查找函数 //查找函数不仅要能查找还要能显示我们分成两个函数因为删除也需要查找 //查找部分 static int find_by_name(const contact* con,const char* arr) //依据名字查找 { assert(con arr);//断言防止空指针 int i 0; for (i 0; i con-count; i)//循环遍历查找 { if (0 strcmp(con-data[i].name, arr)) { return i;//找到返回下标 } } return -1;//找不到返回-1 } //查找 void search_contact(contact* con) { static int find_by_name(const contact * con, const char* arr);//函数声明 assert(con);//断言防止空指针 if (con-count 0)//无联系人情况 { printf(无联系人可查找\n); return; } printf(请输入要查找的人的姓名\n); char arr[NAME] { 0 }; scanf(%s, arr); int a find_by_name(con, arr);//套用查找函数 if (a -1) { printf(要查找的人不存在\n); return; } printf(%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n, 姓名, 年龄, 电话, 性别, 地址); printf(%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n, con-data[a].name, con-data[a].age, con-data[a].tele, con-data[a].sex, con-data[a].addr);//显示部分 } //删除 void del_contact(contact* con) { static int find_by_name(const contact * con, const char* arr);//函数声明 assert(con);//断言防止空指针 char arr[NAME] { 0 }; if (con-count 0)//无联系人情况 { printf(无联系人可删除\n); return; } printf(请输入要删除人的名字\n); scanf(%s, arr); int a find_by_name(con,arr);//函数回调 if (a -1) { printf(要删除的人不存在\n); return; } for (; a con-count - 1; a)//删除本质就是把后面的信息提前覆盖掉这个下标的信息 //当然也还有其他的写法 { con-data[a] con-data[a 1]; } con-count--; printf(删除成功\n); } //修改函数 //修改也要先找 void modify_contact(contact* con) { static int find_by_name(const contact * con, const char* arr);//函数声明 assert(con);//断言防止空指针 if (con-count 0) { printf(无联系人可修改\n);//没有的情况判断 return; } printf(请输入要修改的人的姓名\n); char arr[NAME] { 0 }; scanf(%s, arr); int a find_by_name(con, arr);//调用函数 if (a -1) { printf(要修改的人不存在\n); return; } printf(请输入修改姓名\n); scanf(%s, con-data[a].name); printf(请输入修改年龄\n); scanf(%d, (con-data[a].age)); printf(请输入修改电话\n); scanf(%s, con-data[a].tele); printf(请输入修改性别\n); scanf(%s, con-data[a].sex); printf(请输入修改地址\n); scanf(%s, con-data[a].addr); printf(修改成功\n);//信息的修改 } //排序函数 //这里我使用了qsort static int cmp_peo_by_name(const void* e1, const void* e2)//qsort调用的排序函数 { return strcmp(((const peo*)e1)-name, ((const peo*)e2)-name); } void sort_contact(contact* con)//本体 { static int cmp_peo_by_name(const void* e1, const void* e2);//函数调用 assert(con);//断言 if (con-count 0) { printf(无联系人可排序\n);//没人的情况 return; } printf(正在按姓名排序\n); qsort(con-data, con-count, sizeof(peo), cmp_peo_by_name);//qsort排序 printf(排序成功\n); show_contact(con);//这里不声明是因为这些函数都会在头文件中声明但内些静态函数不会 } //销毁保存清空 void destroycontact(contact* con)//释放内存 { assert(con); free(con-data); con-data NULL; } void save_contact(const contact* con)//保存信息 { assert(con); FILE* p fopen(contact.txt, wb); if (p NULL) { perror(保存失败:save_contact:fopen); return; } fwrite(con-data, sizeof(peo), con-count, p); fclose(p); p NULL; } void clear_contact(contact* con)//清空文件信息和当前结构体信息 { assert(con); FILE* p fopen(contact.txt, w); if (p NULL) return; fclose(p); p NULL; con-count 0; memset(con-data,0, sizeof(peo)); printf(已全部清除\n); }最后我们再在头文件中声明一下这些函数为了调用