前言:
我们可以使用C语言模拟一个通讯录,用来存储联系人的信息。这个通讯录项目有增加联系人、删除联系人信息、查找联系人信息、修改联系人信息、联系人信息列表排序等功能...实现了最基础的增、删、查、改四个功能。
如果用数组来存储联系人信息大小只能是固定的,一旦运行则无法改动。而我们本章所用到的是动态顺序表,可以随意改动,空间不够就增容。
通讯录还有一点就是每次我们运行并输入完联系人信息,退出程序后联系人信息所在内存被回收了,我们有没有什么办法保存联系人的信息,下一次运行时可以依旧可以看到上一次运行时所输入的联系人信息呢?答案是有的。我们可以使用文件操作函数,退出之前将通讯录联系人的信息数据传输到我们指定的文本文件,下一次运行时再通过这个文本文件将数据取出。
那么废话不多说,我们现在就开始!
创建好contact.h头文件:
我们既然想保存人的信息就需要一个自定义类型来表示人的名字、年龄、性别等信息...所以我们需要先创建一个自定义类型来表示一个人的信息。
struct peoinfo
{char name[20];int age;char gender[5];char telephone[12];char address[20];
};
人的信息创建好了,我们是不是需要一个多个该类型的连续空间用来存放多个联系人的信息,而且还需要一个变量来表示当前通讯录容量大小,一个变量来表示已添加联系人的个数,如果直接定义分散开有点麻烦,我们干脆把这几个变量空间都集中起来。
typedef struct contact
{struct peoinfo* peo;//动态顺序表int size;//当前联系人个数int capacity;//通讯录空间容量
}*pcontact;
注:记得把这两个自定义函数放入自己创建的.h为后缀的头文件中,我们不管创建多少个.c为后缀的源文件都可以通过包含该头文件创建这两个自定义类型的变量。
创建好test.c源文件:
首先记得包含头文件,然后就是敲出下面程序:
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void menu()
{printf("*****************************\n");printf("*****1.add 2.del*****\n");printf("*****3.find 4.alter*****\n");printf("*****5.prag 6.sort****\n");printf("*****************************\n");
}
int main()
{int input = 0;struct contact pf;ContactInit(&pf);//通讯录初始化do{menu();printf("请选择:>");scanf("%d", &input);switch(input){case 1:ContactAdd(&pf);//添加联系人break;case 2:ContactDel(&pf);//删除联系人break;case 3:ContactSearch(&pf);//查找联系人break;case 4:ContactAlter(&pf);//修改联系人break;case 5:ContactPrint(&pf);//打印联系人break;case 6:ContactSort(&pf);//联系人列表排序break;case 0:ContactQuit(&pf);//将数据输出到文件printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}} while (input);return 0;
}
下面调用的自定义函数都是声明在头文件,我们包含了头文件就可以通过头文件中的函数声明找到另一个.c为后缀的源文件中调用该自定义函数。
该项目所创建的自定义函数的声明:
void ContactInit(pcontact pf);//初始化void ContactLoad(pcontact pf);//将文件保存的联系人信息数据取出void ContactCheck(pcontact pf);//检查是否超出开辟内存并增容void ContactAdd(pcontact pf);//增加联系人信息void ContactPrint(pcontact pf);//显示所有联系人信息void ContactDel(pcontact pf);//删除联系人信息int Findname(pcontact pf, const char* str);//查找联系人名字,找到联系人所在通讯录位置(下标)void ContactSearch(pcontact pf);//查找联系人信息并显示void ContactAlter(pcontact pf);//修改联系人信息void ContactSort(pcontact pf);//通讯录排序void ContactQuit(pcontact pf);//退出通讯录之前将通讯录所有联系人的信息数据保存下来,下一次打开可以直接载入
然后创建contact.c文件来实现这些自定义函数:
通讯录初识化:
void ContactInit(pcontact pf)
{pf->peo = (struct peoinfo*)malloc(2 * sizeof(struct peoinfo));if (pf->peo == NULL){perror("malloc");return;}pf->capacity = 2;pf->size = 0;ContactLoad(pf);
}
先将我们创建的结构体变量给赋上一个值,先给我们结构体中的联系人动态顺序表一个拥有2个struct peoinfo结构体大小的空间,后面如果不够用了就增容。既然分配了2个struct peoinfo大小的空间,那容量capacity也要设置成2。因为暂时还没有存储联系人size先设置为0,ContactLoad是载入通讯录的意思,是一个自定义函数,是将指定文本文件中的联系人信息的数据载入通讯录,如果没有则不载入。
ContactLoad函数的实现:
void ContactLoad(pcontact pf)
{struct peoinfo p = { 0 };FILE* pe = fopen("C:\\Users\\linlu\\Desktop\\test.txt", "rb");if (pf == NULL){perror("fopen");return;}while (fread(&p, sizeof(struct peoinfo), 1, pe)){ContactCheck(pf);pf->peo[pf->size] = p;pf->size++;}fclose(pe);pe = NULL;
}
首先打开文件,使用pe接收文件信息区的地址,然后通过该地址访问文件将数据输入到变量p,然后将p赋值给pf所指向的联系人动态顺序表,每载入一个联系人信息pf->size需要++一次,ContactCheck是检查通讯录的意思,是自定义函数,是检查pf的容量是否足够,如果容量满了就增容。
ContactCheck自定义函数的实现:
void ContactCheck(pcontact pf)
{if (pf->size == pf->capacity){struct peoinfo* ptr = (struct peoinfo*)realloc(pf->peo, (pf->capacity + 2) * sizeof(struct peoinfo));if (ptr != NULL){(pf->capacity) += 2;pf->peo = ptr;printf("增容成功\n");}else{printf("增容失败\n");}}
}
初始化好后我们就可以自己增加联系人:
void ContactAdd(pcontact pf)
{ContactCheck(pf);//再检查一下容量够不够大printf("请输入名字:>");scanf("%s", pf->peo[pf->size].name);printf("请输入年龄:>");scanf("%d", &(pf->peo[pf->size].age));printf("请输入性别:>");scanf("%s", pf->peo[pf->size].gender);printf("请输入电话:>");scanf("%s", pf->peo[pf->size].telephone);printf("请输入地址:>");scanf("%s", pf->peo[pf->size].address);pf->size++;printf("通讯录已增加联系人\n");
}
增加联系人之前判断一下需要做一下通讯录的容量检查,看一下是否需要增容。
我们自己添加好联系人后可以通过打印来显示:
void ContactPrint(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出");return;}printf("%-5s %-3s %s %-9s %-10s\n", "名字", "年龄", "性别", "电话", "地址");int i = 0;for (i = 0; i < pf->size; i++){printf("%-5s %-3d %-5s %-9s %-5s\n", pf->peo[i].name, pf->peo[i].age, pf->peo[i].gender, pf->peo[i].telephone, pf->peo[i].address);}
}
删除联系人功能:
void ContactDel(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}char str[20] = { 0 };printf("请输入要删除的联系人名字:>");scanf("%s", str);int ret = Findname(pf, str);//查找所有联系人中有没有这个名字,如果有返回对应下标,没有返回-1if (ret == -1){printf("要删除的联系人不存在,退出\n");return;}int i = 0;for (i = ret; i < pf->size - 1; i++){pf->peo[i] = pf->peo[i + 1];//通过覆盖的方式删除}pf->size--;printf("删除成功\n");
}
如果要删除联系人肯定需要通过名字来查找,所以首先必须输入联系人名字,通过这个名字找到联系人信息所在下标位置,返回这个下标位置就可以通过这个位置来删除。所以需要额外定义一个findname(查找名字)自定义函数来查找名字。
注:因为findname的功能实现需要频繁使用,所以将这个功能封装成一个函数供我们使用。
findname自定义函数的实现:
int Findname(pcontact pf, const char* str)
{assert(str != NULL);int i = 0;for (i = 0; i < pf->size; i++){if (strcmp(str, pf->peo[i].name) == 0){return i;}}return -1;//找不到就返回-1
}
查找联系人并显示功能实现:
void ContactSearch(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}char str[20] = { 0 };printf("请输入要查找联系人的名字:>");scanf("%s", str);int ret = Findname(pf, str);if (ret == -1){printf("要查找联系人不存在,退出\n");return;}printf("%-5s %-3s %s %-9s %-10s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-5s %-3d %-5s %-9s %-5s\n", pf->peo[ret].name, pf->peo[ret].age, pf->peo[ret].gender, pf->peo[ret].telephone, pf->peo[ret].address);
}
修改联系人功能实现:
void ContactAlter(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}char str[20] = { 0 };printf("请输入要修改的联系人的名字:>");scanf("%s", str);int ret = Findname(pf, str);if (ret == -1){printf("要修改联系人不存在,退出:>");return;}printf("请输入名字:>");scanf("%s", pf->peo[ret].name);printf("请输入年龄:>");scanf("%d", &(pf->peo[ret].age));printf("请输入性别:>");scanf("%s", pf->peo[ret].gender);printf("请输入电话:>");scanf("%s", pf->peo[ret].telephone);printf("请输入地址:>");scanf("%s", pf->peo[ret].address);printf("已修改联系人信息\n");
}
联系人列表排序功能实现:
int cmp_stu(const void* e1, const void* e2)
{return strcmp((char*)e1, (char*)e2);
}
void ContactSort(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}qsort(pf->peo,pf->size,sizeof(struct peoinfo),cmp_stu);printf("排序成功");
}
使用qsort函数来排序,qsort是一个库函数,前几篇博客有介绍,如果不会可以去看一下。
退出通讯录,保存联系人信息数据:
void ContactQuit(pcontact pf)
{if (pf->size == 0){return;}FILE* pe = fopen("C:\\Users\\linlu\\Desktop\\test.txt", "wb");if (pe == NULL){perror("fopen");return;}int i = 0;for (i = 0; i < pf->size; i++){fwrite(&pf->peo[i], sizeof(struct peoinfo), 1, pe);}fclose(pe);pe = NULL;
}
到了这里整个通讯录模拟实现也就结束了。
通讯录模拟实现完整代码
contact.h 头文件声明
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
struct peoinfo
{char name[20];int age;char gender[5];char telephone[12];char address[20];
};
typedef struct contact
{struct peoinfo* peo;int size;int capacity;
}*pcontact;
void ContactInit(pcontact pf);//初始化void ContactLoad(pcontact pf);//将文件保存的联系人信息数据取出void ContactCheck(pcontact pf);//检查是否超出开辟内存并增容void ContactAdd(pcontact pf);//增加联系人信息void ContactPrint(pcontact pf);//显示所有联系人信息void ContactDel(pcontact pf);//删除联系人信息int Findname(pcontact pf, const char* str);//查找联系人名字,找到联系人所在通讯录位置(下标)void ContactSearch(pcontact pf);//查找联系人信息并显示void ContactAlter(pcontact pf);//修改联系人信息void ContactSort(pcontact pf);//通讯录排序void ContactQuit(pcontact pf);//退出通讯录之前将通讯录所有联系人的信息数据保存下来,下一次打开可以直接载入
contact.c源文件自定义函数的实现
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
//通讯录初识化
void ContactInit(pcontact pf)
{pf->peo = (struct peoinfo*)malloc(2 * sizeof(struct peoinfo));if (pf->peo == NULL){perror("malloc");return;}pf->capacity = 2;pf->size = 0;ContactLoad(pf);
}//载入上一次运行保存的通讯录数据
void ContactLoad(pcontact pf)
{struct peoinfo p = { 0 };FILE* pe = fopen("C:\\Users\\linlu\\Desktop\\test.txt", "rb");if (pf == NULL){perror("fopen");return;}while (fread(&p, sizeof(struct peoinfo), 1, pe)){ContactCheck(pf);pf->peo[pf->size] = p;pf->size++;}fclose(pe);pe = NULL;
}//通讯录空间增容
void ContactCheck(pcontact pf)
{if (pf->size == pf->capacity){struct peoinfo* ptr = (struct peoinfo*)realloc(pf->peo, (pf->capacity + 2) * sizeof(struct peoinfo));if (ptr != NULL){(pf->capacity) += 2;pf->peo = ptr;printf("增容成功\n");}else{printf("增容失败\n");}}
}//添加联系人
void ContactAdd(pcontact pf)
{ContactCheck(pf);//再检查一下容量够不够大printf("请输入名字:>");scanf("%s", pf->peo[pf->size].name);printf("请输入年龄:>");scanf("%d", &(pf->peo[pf->size].age));printf("请输入性别:>");scanf("%s", pf->peo[pf->size].gender);printf("请输入电话:>");scanf("%s", pf->peo[pf->size].telephone);printf("请输入地址:>");scanf("%s", pf->peo[pf->size].address);pf->size++;printf("通讯录已增加联系人\n");
}//打印联系人信息
void ContactPrint(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出");return;}printf("%-5s %-3s %s %-9s %-10s\n", "名字", "年龄", "性别", "电话", "地址");int i = 0;for (i = 0; i < pf->size; i++){printf("%-5s %-3d %-5s %-9s %-5s\n", pf->peo[i].name, pf->peo[i].age, pf->peo[i].gender, pf->peo[i].telephone, pf->peo[i].address);}
}//查找联系人姓名
int Findname(pcontact pf, const char* str)
{assert(str != NULL);int i = 0;for (i = 0; i < pf->size; i++){if (strcmp(str, pf->peo[i].name) == 0){return i;}}return -1;
}
//删除联系人
void ContactDel(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}char str[20] = { 0 };printf("请输入要删除的联系人名字:>");scanf("%s", str);int ret = Findname(pf, str);//查找所有联系人中有没有这个名字,如果有返回对应下标,没有返回-1if (ret == -1){printf("要删除的联系人不存在,退出\n");return;}int i = 0;for (i = ret; i < pf->size - 1; i++){pf->peo[i] = pf->peo[i + 1];//通过覆盖的方式删除}pf->size--;printf("删除成功\n");
}//查找联系人
void ContactSearch(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}char str[20] = { 0 };printf("请输入要查找联系人的名字:>");scanf("%s", str);int ret = Findname(pf, str);if (ret == -1){printf("要查找联系人不存在,退出\n");return;}printf("%-5s %-3s %s %-9s %-10s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-5s %-3d %-5s %-9s %-5s\n", pf->peo[ret].name, pf->peo[ret].age, pf->peo[ret].gender, pf->peo[ret].telephone, pf->peo[ret].address);
}//修改联系人
void ContactAlter(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}char str[20] = { 0 };printf("请输入要修改的联系人的名字:>");scanf("%s", str);int ret = Findname(pf, str);if (ret == -1){printf("要修改联系人不存在,退出:>");return;}printf("请输入名字:>");scanf("%s", pf->peo[ret].name);printf("请输入年龄:>");scanf("%d", &(pf->peo[ret].age));printf("请输入性别:>");scanf("%s", pf->peo[ret].gender);printf("请输入电话:>");scanf("%s", pf->peo[ret].telephone);printf("请输入地址:>");scanf("%s", pf->peo[ret].address);printf("已修改联系人信息\n");
}//联系人列表排序
int cmp_stu(const void* e1, const void* e2)
{return strcmp((char*)e1, (char*)e2);
}
void ContactSort(pcontact pf)
{if (pf->size == 0){printf("通讯录为空,退出\n");return;}qsort(pf->peo,pf->size,sizeof(struct peoinfo),cmp_stu);printf("排序成功");
}//退出通讯录,保存通讯录数据
void ContactQuit(pcontact pf)
{if (pf->size == 0){return;}FILE* pe = fopen("C:\\Users\\linlu\\Desktop\\test.txt", "wb");if (pe == NULL){perror("fopen");return;}int i = 0;for (i = 0; i < pf->size; i++){fwrite(&pf->peo[i], sizeof(struct peoinfo), 1, pe);}fclose(pe);pe = NULL;
}
test.c源文件调用自定义函数
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void menu()
{printf("*****************************\n");printf("*****1.add 2.del*****\n");printf("*****3.find 4.alter*****\n");printf("*****5.prag 6.sort****\n");printf("*****************************\n");
}
int main()
{int input = 0;struct contact pf;ContactInit(&pf);//初始化do{menu();printf("请选择:>");scanf("%d", &input);switch(input){case 1:ContactAdd(&pf);//增加break;case 2:ContactDel(&pf);//删除break;case 3:ContactSearch(&pf);//查找break;case 4:ContactAlter(&pf);//修改break;case 5:ContactPrint(&pf);//显示break;case 6:ContactSort(&pf);//排序break;case 0:ContactQuit(&pf);//保存printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}} while (input);return 0;
}
完 end