通讯录小练习:柔性数组和文件操作实现
目录
一.程序功能
二.定义关键类型的头文件与枚举的应用
三.封装柔性数组的增容函数与缩容函数
四.添加联系人功能模块
五 .联系人信息打印模块
六. 查找指定联系人的模块
七.删除指定联系人模块
八.修改指定联系人信息模块
九.排序模块
九.文件操作模块
十.通讯录初始化模块
十一.代码总览
contact.h头文件
contactfunction.h头文件
通讯录基本功能模块源文件
文件操作模块源文件
排序模块源文件:
主函数测试模块源文件:
一.程序功能
实现一个通讯录;
通讯录可以用来存储联系人的信息包括:姓名、性别、年龄、电话、住址
提供方法:
1.添加联系人信息
2.删除指定联系人信息
3.查找指定联系人信息
4.修改指定联系人信息
5.显示所有联系人信息
6.清空所有联系人
7.根据姓名 或 性别或 年龄 或 电话 或 住址 排序所有联系人
二.定义关键类型的头文件与枚举的应用
将存储联系人信息的结构体中各成员数组的大小封装为枚举常量,提高代码的可维护性
PeoIfo是存储联系人信息的结构体
listIfo是含有PeoIfo结构体柔性数组的结构体,通过一个listIfo结构体就可以维护整个通讯录列表,listIfo结构体中的Peonum成员用于维护柔性数组Conlist的高地址边界。
由于测试主程序要用到 switch语句,所以这里把菜单选项对应的数字定义为枚举常量来增强代码的可读性
contact .h #include <stdio.h> #include <string.h> #include <assert.h> #include <memory.h> #include <errno.h> #include <stdlib.h> enum listchoice /*定义一系列枚举常量 代替宏来表示菜单的选项数字*/ { Add = 1, Del, Search, Modify, show, reset, sort, Exit = 0 }; enum PeoDataSize /*定义一系列枚举常量 代替宏来表示各个信息的字节数*/ { NameSize = 20, SexSize = 20, AgeSize = 20, TelSize = 20, AddrSize = 20, }; typedef struct PeoIfo //存储联系人信息的结构体 { char Name[NameSize]; char Sex[SexSize]; char Age[AgeSize]; char Tel[TelSize]; char Addr[AddrSize]; }PeoIfo; typedef struct listIfo { int Peonum; //记录联系人个数 PeoIfo Conlist[]; //使用柔性数组 }listIfo;
在主程序中可以通过这样一段代码创建IistIfo结构体, 初始状态下,柔性数组Conlist的元素个数为0,后续柔性数组的大小可以通过realloc进行调整。
主函数测试模块总览:
int main() { listIfo* plist = (listIfo*)malloc(sizeof(listIfo)); //创建柔性数组结构体 if (NULL == plist) { perror("malloc:"); return 1; } plist->Peonum = 0; plist=ReadFile(plist); //从文件中读取联系人信息 int input = 0; do { menu(); if(scanf("%d", &input)); //选择执行的功能 switch (input) { case Add: plist=ADD(plist); break; case Del: plist=DEL(plist); break; case Search: PrintNum(Search_byname(plist), plist); break; case Modify: MODIF(plist); break; case show: Show(plist); break; case reset: plist = InitList(plist); break; case sort: bubble_sort(plist->Conlist, plist->Peonum, sizeof(PeoIfo), sort_choice()); break; case Exit: Writefile(plist); //销毁通讯录前先将联系人信息保存到文件中 free(plist); //退出程序销毁结构体 plist = NULL; break; default: printf(" invalue input,please retry\n"); break; } } while (input); return 0; }
三.封装柔性数组的增容函数与缩容函数
由于增容和缩容的功能在很多地方都可能会被使用,因此将它们封装成函数是十分有必要的,可以很大程度上提高代码的简洁性和可维护性。
listIfo* IncreSize (listIfo*ListInfo) //扩容函数 { listIfo* tem = (listIfo *)realloc(ListInfo, (sizeof(listIfo) + sizeof(PeoIfo)*(size_t)((ListInfo->Peonum)+1))); if (tem == NULL) //空间调整失败则返回原指针 { perror("reallocInc:"); return ListInfo; } return tem; } listIfo* DecreSize(listIfo* ListInfo) //缩容函数 { if (ListInfo->Peonum == 0) { return ListInfo; } listIfo* tem = (listIfo*)realloc(ListInfo, (sizeof(listIfo) + sizeof(PeoIfo) * ListInfo->Peonum-1)); if (tem == NULL) //空间调整失败则返回原指针 { perror("reallocInc:"); return ListInfo; } return tem; }
使用realloc函数注意判断空间动态调整是否成功
两个函数都要返回新调整后的空间的地址
四.添加联系人功能模块
listIfo* ADD(listIfo* ListInfo) { ListInfo = IncreSize(ListInfo); 调用增容函数使柔性数组增加一个元素 memset((ListInfo->Conlist) + (ListInfo->Peonum), 0, sizeof(PeoIfo)); int ret = 0; printf("请输入姓名:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Name); printf("请输入性别:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Sex); printf("请输入年龄:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Age); printf("请输入电话:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Tel); printf("请输入地址:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Addr); ListInfo->Peonum++; 联系人信息填完后记得令人数自增 printf("ADD success\n"); return ListInfo; }
五 .联系人信息打印模块
void Show(listIfo* ListInfo) { int i = 0; printf("%-10s %-4s %-3s %-10s %-10s \n", "姓名", "性别", "年龄", "电话", "地址"); for (i = 0; i < ListInfo->Peonum; i++) { printf("%-10s ", ListInfo->Conlist[i].Name); printf("%-4s ", ListInfo->Conlist[i].Sex); printf("%-3s ", ListInfo->Conlist[i].Age); printf("%-10s ", ListInfo->Conlist[i].Tel); printf("%-10s ", ListInfo->Conlist[i].Addr); printf("\n"); } }
六. 查找指定联系人的模块
int Search_byname(listIfo* ListIfo)
{
int i = 0;
char name[NameSize] = { 0 };
printf("输入联系人姓名:>");
int ret=scanf("%s", name);
for (i = 0; i < ListIfo->Peonum; i++)
{
if (strcmp(name, ListIfo->Conlist[i].Name) == 0)
{
return i;
}
}
printf("联系人不存在\n");
return -1;
}
Search_byname函数返回柔性数组对应元素的下标
Search_byname函数同样在很多其他模块可能被调用
打印单个联系人信息的函数
void PrintNum(int i, listIfo* ListIfo) { printf("%-10s ", ListIfo->Conlist[i].Name); printf("%-4s ", ListIfo->Conlist[i].Sex); printf("%-3s ", ListIfo->Conlist[i].Age); printf("%-10s ", ListIfo->Conlist[i].Tel); printf("%-10s ", ListIfo->Conlist[i].Addr); printf("\n"); }
Search_byname 和 PrintNum函数一起使用可以完成查找指定联系人信息的功能
七.删除指定联系人模块
listIfo* DEL(listIfo* ListIfo) { int ret = Search_byname(ListIfo); if (ret != -1) { memmove((ListIfo->Conlist) + ret, (ListIfo->Conlist) + ret + 1, sizeof(PeoIfo) * (long long)((ListIfo->Peonum) - ret - 1)); ListIfo = DecreSize(ListIfo); //调用缩容函数 ListIfo->Peonum--; printf("DELATE success\n"); } return ListIfo; }
八.修改指定联系人信息模块
void MODIF(listIfo* ListIfo) { int ret = Search_byname(ListIfo); 调用查找函数 if (ret != -1) { printf("请输入姓名:>"); int i=scanf("%s", (ListIfo->Conlist[ret]).Name); printf("请输入性别:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Sex); printf("请输入年龄:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Age); printf("请输入电话:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Tel); printf("请输入地址:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Addr); printf("modify success\n"); } }
九.排序模块
排序模块函数比较多,因此单独为其创建了一个源文件。
这里我们使用模拟qsort的冒泡排序函数来实现通讯录排序功能。
首先是一系列比较函数:
int cmpbyname(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Name, ((PeoIfo*)e2)->Name); } int cmpbysex(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Sex, ((PeoIfo*)e2)->Sex); } int cmpbyage(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Age, ((PeoIfo*)e2)->Age); } int cmpbytel(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Tel, ((PeoIfo*)e2)->Tel); } int cmpbyaddr(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Addr, ((PeoIfo*)e2)->Addr); }
然后设计一个函数可以让用户自行选择根据何种信息排序,该函数的返回值是一个函数指针类型为:int (*)(void * , void *),为了代码的简洁性,我们使用函数指针数组构建转移表。
#define ChoiceNum 5 static void sortmenu(void) { printf("***********************************\n"); printf("***** 1.sort by name ****\n"); printf("***** 2.sort by sex ****\n"); printf("***** 3.sort by Age ****\n"); printf("***** 4.sort by Tel ****\n"); printf("***** 5.sort by addr ****\n"); printf("***********************************\n"); }
int (*sort_choice(void))(void*, void*) { int input = 0; int (*Funarr[ChoiceNum])(void*, void*) = { cmpbyname, cmpbysex, cmpbyage, cmpbytel, cmpbyaddr }; 转移表 sortmenu(); int ret = scanf("%d", &input); while (input<0 || input >ChoiceNum - 1) { printf("invalued input,please retry\n"); sortmenu(); } return *(Funarr+input - 1); 返回对应的比较函数 }
多功能bubble_sort模块
static void swap(void* e1, void* e2,int width) { int i = 0; for (i = 0; i < width; i++) { char tem = *((char*)e1 + i); *((char*)e1 + i) = *((char*)e2 + i); *((char*)e2 + i) = tem; } } void* bubble_sort(void* base, long long num, int width, int(*cmp)(void*, void*)) { int i = 0; long long j = 0; int flag = 0; for (i = 0; i < num-1; i++) { flag = 1; for (j = 0; j < num - 1 - i; j++) { if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { flag = 0; swap((char*)base + j * width, (char*)base + (j + 1) * width,width); } } if (flag) { break; } } printf("sorted success\n"); return base; }
在主函数中以如下形式调用bubble_sort便可完成根据任意联系人信息进行排序的功能
bubble_sort(plist->Conlist, plist->Peonum, sizeof(PeoIfo), sort_choice());
九.文件操作模块
运行程序并创建了含柔性数组成员的结构体后,需要将文件中的联系人信息读入柔性数组中。我们采用二进制读写来完成通讯录的文件操作。
文件读取模块:
listIfo* ReadFile(listIfo* ListIfo) { FILE* pfile = fopen("text.txt", "rb"); 打开文件并检验是否发生错误 if (NULL == pfile) { perror("fopen:"); return ListIfo; } ListIfo = IncreSize(ListIfo); 读取文件前先为0个元素的柔性数组增容 while (fread(((ListIfo->Conlist) + (ListIfo->Peonum)), sizeof(PeoIfo), 1, pfile)) { ListIfo->Peonum++; ListIfo = IncreSize(ListIfo); 每读取一个联系人增容一次 } if (ferror(pfile)) 检验读取是否成功 { printf("reading error occurred\n"); } else if (feof(pfile)) { printf("readfile success\n"); } fclose(pfile); pfile = NULL; return ListIfo; 关闭文件并 返回调整后的柔性数组结构体的地址 }
程序结束前需要将柔性数组中存储的信息以二进制数据的形式写入文件中。
文件写入模块:
void Writefile(listIfo* ListIfo) { FILE* pfile = fopen("text.txt", "wb"); if (NULL == pfile) { perror("fopen:"); return ; } int i = 0; for (i = 0; i < ListIfo->Peonum; i++) 将每个联系人信息写入文件中 { if (!fwrite(((ListIfo->Conlist) + i), sizeof(PeoIfo), 1, pfile)) { perror("fwrite:"); return; } } printf("contact successfully saved\n"); fclose(pfile); pfile = NULL; return; }
十.通讯录初始化模块
listIfo * InitList(listIfo* ListIfo) { listIfo* tem = NULL; tem = (listIfo*)realloc(ListIfo, sizeof(listIfo)); 将柔性数组调整为0个元素 if (tem == NULL) { perror("realloc and init failed:"); return ListIfo; } tem->Peonum = 0; printf("Init success\n"); return tem; }
十一.代码总览
contact.h头文件
#pragma once //contact .h #include <stdio.h> #include <string.h> #include <assert.h> #include <memory.h> #include <errno.h> #include <stdlib.h> enum listchoice /*定义一系列枚举常量 代替宏来表示菜单的选项数字*/ { Add = 1, Del, Search, Modify, show, reset, sort, Exit = 0 }; enum PeoDataSize /*定义一系列枚举常量 代替宏来表示各个信息的字节数*/ { NameSize = 20, SexSize = 20, AgeSize = 20, TelSize = 20, AddrSize = 20, }; typedef struct PeoIfo //存储联系人信息的结构体 { char Name[NameSize]; char Sex[SexSize]; char Age[AgeSize]; char Tel[TelSize]; char Addr[AddrSize]; }PeoIfo; typedef struct listIfo { int Peonum; //存储的联系人数 PeoIfo Conlist[]; //使用柔性数组 }listIfo;
contactfunction.h头文件
#include "contact.h" #pragma once void menu(void); listIfo* ADD(listIfo* ListInfo); void Show(listIfo* ListIfo); listIfo* InitList(listIfo* ListIfo); int Search_byname(listIfo* ListIfo); void PrintNum(int i, listIfo* ListIfo); listIfo* DEL(listIfo* ListIfo); void MODIF(listIfo* ListIfo); void* bubble_sort(void* base, long long num, int width, int(*cmp)(void*, void*)); listIfo* DecreSize(listIfo* ListInfo); listIfo* IncreSize(listIfo* ListInfo); int (*sort_choice(void))(void*, void*); listIfo* ReadFile(listIfo* ListIfo); void Writefile(listIfo* ListIfo);
通讯录基本功能模块源文件
#include "contact.h" #include "contactfunction.h" //封装菜单函数 void menu(void) { printf("*********************************\n"); printf("****** 1.Add contact *****\n"); printf("****** *****\n"); printf("****** 2.del contact *****\n"); printf("****** *****\n"); printf("****** 3.search contact *****\n"); printf("****** *****\n"); printf("****** 4.modify contact *****\n"); printf("****** *****\n"); printf("****** 5.show list *****\n"); printf("****** *****\n"); printf("****** 6.reset list *****\n"); printf("****** *****\n"); printf("****** 7.sort *****\n"); printf("****** *****\n"); printf("****** 0.exit *****\n"); printf("*********************************\n"); printf("---------please choose-----------\n"); } listIfo* IncreSize (listIfo*ListInfo) //扩容函数 { listIfo* tem = (listIfo *)realloc(ListInfo, (sizeof(listIfo) + sizeof(PeoIfo)*(size_t)((ListInfo->Peonum)+1))); if (tem == NULL) //空间调整失败则返回原指针 { perror("reallocInc:"); return ListInfo; } return tem; } listIfo* DecreSize(listIfo* ListInfo) //缩容函数 { if (ListInfo->Peonum == 0) { return ListInfo; } listIfo* tem = (listIfo*)realloc(ListInfo, (sizeof(listIfo) + sizeof(PeoIfo) * ListInfo->Peonum-1)); if (tem == NULL) //空间调整失败则返回原指针 { perror("reallocInc:"); return ListInfo; } return tem; } //void InitListNUM(listIfo* ListIfo) //{ // memset((ListIfo->Conlist)+(ListIfo->Peonum), 0, sizeof(PeoIfo)); // memset(ListIfo->Conlist[ListIfo->Peonum].Sex, 0, SexSize); // memset(ListIfo->Conlist[ListIfo->Peonum].Age, 0, AgeSize); // memset(ListIfo->Conlist[ListIfo->Peonum].Tel, 0, TelSize); // memset(ListIfo->Conlist[ListIfo->Peonum].Addr, 0, AddrSize); //} listIfo* ADD(listIfo* ListInfo) { ListInfo = IncreSize(ListInfo); memset((ListInfo->Conlist) + (ListInfo->Peonum), 0, sizeof(PeoIfo)); int ret = 0; printf("请输入姓名:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Name); printf("请输入性别:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Sex); printf("请输入年龄:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Age); printf("请输入电话:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Tel); printf("请输入地址:>"); ret = scanf("%s", (ListInfo->Conlist[ListInfo->Peonum]).Addr); ListInfo->Peonum++; printf("ADD success\n"); return ListInfo; } listIfo * InitList(listIfo* ListIfo) { listIfo* tem = NULL; tem = (listIfo*)realloc(ListIfo, sizeof(listIfo)); if (tem == NULL) { perror("realloc and init failed:"); return ListIfo; } tem->Peonum = 0; printf("Init success\n"); return tem; } void Show(listIfo* ListInfo) { int i = 0; printf("%-10s %-4s %-3s %-10s %-10s \n", "姓名", "性别", "年龄", "电话", "地址"); for (i = 0; i < ListInfo->Peonum; i++) { printf("%-10s ", ListInfo->Conlist[i].Name); printf("%-4s ", ListInfo->Conlist[i].Sex); printf("%-3s ", ListInfo->Conlist[i].Age); printf("%-10s ", ListInfo->Conlist[i].Tel); printf("%-10s ", ListInfo->Conlist[i].Addr); printf("\n"); } } int Search_byname(listIfo* ListIfo) { int i = 0; char name[NameSize] = { 0 }; printf("输入联系人姓名:>"); int ret=scanf("%s", name); for (i = 0; i < ListIfo->Peonum; i++) { if (strcmp(name, ListIfo->Conlist[i].Name) == 0) { return i; } } printf("联系人不存在\n"); return -1; } void PrintNum(int i, listIfo* ListIfo) { printf("%-10s ", ListIfo->Conlist[i].Name); printf("%-4s ", ListIfo->Conlist[i].Sex); printf("%-3s ", ListIfo->Conlist[i].Age); printf("%-10s ", ListIfo->Conlist[i].Tel); printf("%-10s ", ListIfo->Conlist[i].Addr); printf("\n"); } listIfo* DEL(listIfo* ListIfo) { int ret = Search_byname(ListIfo); if (ret != -1) { memmove((ListIfo->Conlist) + ret, (ListIfo->Conlist) + ret + 1, sizeof(PeoIfo) * (long long)((ListIfo->Peonum) - ret - 1)); ListIfo = DecreSize(ListIfo); //调用缩容函数 ListIfo->Peonum--; printf("DELATE success\n"); } return ListIfo; } void MODIF(listIfo* ListIfo) { int ret = Search_byname(ListIfo); if (ret != -1) { printf("请输入姓名:>"); int i=scanf("%s", (ListIfo->Conlist[ret]).Name); printf("请输入性别:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Sex); printf("请输入年龄:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Age); printf("请输入电话:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Tel); printf("请输入地址:>"); i = scanf("%s", (ListIfo->Conlist[ret]).Addr); printf("modify success\n"); } }
文件操作模块源文件
#include "contactfunction.h" #include "contact.h" //文件的读写我们采用二进制读写 listIfo* ReadFile(listIfo* ListIfo) { FILE* pfile = fopen("text.txt", "rb"); if (NULL == pfile) { perror("fopen:"); return ListIfo; } ListIfo = IncreSize(ListIfo); while (fread(((ListIfo->Conlist) + (ListIfo->Peonum)), sizeof(PeoIfo), 1, pfile)) { ListIfo->Peonum++; ListIfo = IncreSize(ListIfo); } if (ferror(pfile)) { printf("reading error occurred\n"); } else if (feof(pfile)) { printf("readfile success\n"); } fclose(pfile); pfile = NULL; return ListIfo; } void Writefile(listIfo* ListIfo) { FILE* pfile = fopen("text.txt", "wb"); if (NULL == pfile) { perror("fopen:"); return ; } int i = 0; for (i = 0; i < ListIfo->Peonum; i++) { if (!fwrite(((ListIfo->Conlist) + i), sizeof(PeoIfo), 1, pfile)) { perror("fwrite:"); return; } } printf("contact successfully saved\n"); fclose(pfile); pfile = NULL; return; }
排序模块源文件:
#include "contact.h" #include "contactfunction.h" #define ChoiceNum 5 static void sortmenu(void) { printf("***********************************\n"); printf("***** 1.sort by name ****\n"); printf("***** 2.sort by sex ****\n"); printf("***** 3.sort by Age ****\n"); printf("***** 4.sort by Tel ****\n"); printf("***** 5.sort by addr ****\n"); printf("***********************************\n"); } int cmpbyname(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Name, ((PeoIfo*)e2)->Name); } int cmpbysex(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Sex, ((PeoIfo*)e2)->Sex); } int cmpbyage(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Age, ((PeoIfo*)e2)->Age); } int cmpbytel(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Tel, ((PeoIfo*)e2)->Tel); } int cmpbyaddr(void* e1, void* e2) { return strcmp(((PeoIfo*)e1)->Addr, ((PeoIfo*)e2)->Addr); } int (*sort_choice(void))(void*, void*) { int input = 0; int (*Funarr[ChoiceNum])(void*, void*) = { cmpbyname ,cmpbysex,cmpbyage,cmpbytel,cmpbyaddr }; sortmenu(); int ret = scanf("%d", &input); while (input<0 || input >ChoiceNum - 1) { printf("invalued input,please retry\n"); sortmenu(); } return *(Funarr+input - 1); } static void swap(void* e1, void* e2,int width) { int i = 0; for (i = 0; i < width; i++) { char tem = *((char*)e1 + i); *((char*)e1 + i) = *((char*)e2 + i); *((char*)e2 + i) = tem; } } void* bubble_sort(void* base, long long num, int width, int(*cmp)(void*, void*)) { int i = 0; long long j = 0; int flag = 0; for (i = 0; i < num-1; i++) { flag = 1; for (j = 0; j < num - 1 - i; j++) { if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { flag = 0; swap((char*)base + j * width, (char*)base + (j + 1) * width,width); } } if (flag) { break; } } printf("sorted success\n"); return base; }
主函数测试模块源文件:
#include "contact.h" #include "contactfunction.h" int main() { listIfo* plist = (listIfo*)malloc(sizeof(listIfo)); //创建柔性数组结构体 if (NULL == plist) { perror("malloc:"); return 1; } plist->Peonum = 0; plist=ReadFile(plist); int input = 0; do { menu(); if(scanf("%d", &input)); switch (input) { case Add: plist=ADD(plist); break; case Del: plist=DEL(plist); break; case Search: PrintNum(Search_byname(plist), plist); break; case Modify: MODIF(plist); break; case show: Show(plist); break; case reset: plist = InitList(plist); break; case sort: bubble_sort(plist->Conlist, plist->Peonum, sizeof(PeoIfo), sort_choice()); break; case Exit: Writefile(plist); free(plist); //退出程序销毁结构体 plist = NULL; break; default: printf(" invalue input,please retry\n"); break; } } while (input); return 0; }