一、前言
模拟实现通讯录系统,涉及多种循环,枚举,指针,结构体等,很好的将我们的知识串联起来,是一次有意义的练习实践。
本文将用C语言模拟实现一个通讯录系统,其功能包括添加联系人,删除指定联系人,查找指定联系人,修改指定联系人等功能,可以根据需要再自行添加功能。
(源代码附在末尾)
二、通讯录的实现
1.大体框架
(1)菜单的实现
用多个printf打印一个简单的菜单界面
(2)联系人结构体的创建
将我们会使用到的一些变量进行声明,例如最大联系人个数、地址长度。
创建一个名为PeoInfo的结构体,成员有name,age,sex,addr,tele,即联系人的姓名,年龄,性别,地址,电话号码,来表示一个联系人的信息。
再创建一个Contact结构体,来保存PeoInfo结构体写入的联系人信息和联系人个数。
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
初始化数组,结构体置为0,大小置为0。
(3)菜单选项功能
用枚举,给上述符号赋值,可以与菜单对应,并且直观明了。
do
{
menu();
printf("请选择>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEACHER:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case EMPTY:
EmptyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
} while (input);
使用do while语句和switch语句,实现选择菜单中的不同功能。
2.通讯录的功能实现
(1)添加通讯录联系人
void AddContact(Contact* pc)
{
assert(pc);
if (pc->sz == MAX)
{
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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
先判断通讯录是否未满 ,如果满了,提示并退出 如果没满,则直接使用scanf来填写联系人的信息
(2)删除指定联系人
void DelContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
printf("请输入你想删除的联系人的姓名:>");
scanf("%s", name);
int ret = FindContact(pc, name);
if (-1 == ret)
{
printf("通讯录中没有此人\n");
return;
}
int i = 0;
for (i = ret; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
首先判断通讯录是否为空,为空返回,不为空则继续进行。 输入想要删除的人的姓名,然后通过FindContact函数去在通讯录中查找,若没找到,返回-1,若找到了,返回要删除人在结构体数组的序号,用ret接收。再判断ret的值,为-1报错,否则继续下面代码。
定义一个变量i,等于要删除人在结构体数组的序号,将后面一位的信息替换到这个人,通过循环实现将指定人后的每一位成员信息提前一位,最后再将sz(联系人个数)减一。
FindContact函数
int FindContact(const Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
使用for对结构体数组进行遍历,用strcmp函数判断pc->data[i].name(结构体数组中元素的名字)和 name(要查询的名字)是否相等,相等返回0,在if中成立,则函数返回指定人在结构体数组的序号。若遍历完也没有找到,函数则返回-1。
strcmp函数 两个字符串自左向右逐个字符相比(按 ASCII 值大小相比较),直到出现不同的字符或遇 \0 为止。
(3)查找指定联系人
void SearchContact(const Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法查找\n");
return;
}
printf("请输入你想查找的联系人的姓名:>");
scanf("%s", name);
int pos = FindContact(pc, name);
if (-1 == pos)
{
printf("通讯录中没有此人\n");
return;
}
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].tele);
}
同删除指定联系人一样的思路,先找到,然后将指定人信息打印在屏幕上。
(4)修改指定联系人
void ModifyContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法修改\n");
return;
}
printf("请输入你想修改的联系人的姓名:>");
scanf("%s", name);
int pos = FindContact(pc, name);
if (-1 == pos)
{
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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pos].tele);
printf("修改完成\n");
}
同删除指定联系人一样的思路,先找到,然后将指定人要修改的信息替换过去。
(5)显示通讯录的信息
void ShowContact(const Contact* pc)
{
assert(pc);
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
用for循环,将所有联系人的信息打印在屏幕上。
(6)清空所有联系人
void EmptyContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
printf("联系人已清空\n");
}
将联系人个数sz置为0,用memset函数将pc指向的data数组的sizeof(pc->data)个字节置为0
(7)以名字排序所有联系人
void SortContact(Contact* pc)
{
int i = 0;
int j = 0;
struct PeoInfo temp;
for (i = 0; i < pc->sz - 1; i++)
for (j = 0; j < pc->sz - 1 - i; j++)
{
if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
{
temp = pc->data[j];
pc->data[j] = pc->data[j + 1];
pc->data[j + 1] = temp;
}
}
printf("排序完成\n");
}
定义一个结构体数组temp, 利用冒泡排序的思想进行排序
strcmp函数 来比较拼音顺序
- 如果返回值小于 0,则表示 str1 小于 str2。
- 如果返回值大于 0,则表示 str1 大于 str2。
- 如果返回值等于 0,则表示 str1 等于 str2。
三.通讯录的优化
1.动态增容
为了节省空间,更高效的利用空间,我们采用动态的开辟空间,即根据我们的需要,
去开辟一定大小的空间,而不是直接开辟空间去使用
我们要对创建的结构体进行修改,增加capacity变量来记录当前通讯录的最大容量。
void check_capacity(Contact* pc)
{
if (pc->sz == pc->capacity)
{
//增加容量
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
if (ptr == NULL)
{
perror("check_capacity::realloc");
return;
}
pc->data = ptr;
pc->capacity += INC_SZ;
printf("增容成功\n");
}
}
定义一个函数check_capacity进行动态开辟空间。
如果联系人个数等于通讯录最大容量,则进行增容。用realloc函数,为data开辟(pc->capacity + INC_SZ) * sizeof(PeoInfo) ((最大容量+要增加的)*(一个联系人结构体的大小))个字节的空间。
(1)初始化通讯录
//静态版本
//void InitContact(Contact* pc)
//{
// assert(pc);
// pc->sz = 0;
// memset(pc->data, 0, sizeof(pc->data));
//}
//动态版本
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
if (ptr == NULL)
{
perror("InitContact::calloc");
return;
}
pc->data = ptr;
pc->capacity = DEFAULT_SZ;
}
在初始化的时候就要开辟空间,为后面使用做准备。
(2)增加联系人
void AddContact(Contact* pc)
{
assert(pc);
check_capacity(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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
动态版的AddContact函数不需要判断通讯录是否满了,调用check_capacity函数,如果内存满了则增容,然后再添加联系人信息。
(3)清空所有联系人
void EmptyContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
pc->capacity=0;//
printf("联系人已清空\n");
}
在此函数中,再加上capacity置为0即可。
2.写入文件
为了我们通讯录能够保存数据,在我们下次启动程序时仍然能够使用,我们引入文件,
将联系人信息存到文本文件中,运行程序先打开文件,然后读取,再可以对其进行修改,
最后退出前,将信息保存到文件中。
(1)初始化时,读取文件信息
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
if (ptr == NULL)
{
perror("InitContact::calloc");
return;
}
pc->data = ptr;
pc->capacity = DEFAULT_SZ;
//加载文件信息到通讯录
LoadContact(pc);
}
初始化之后再通过LoadContact函数,将文件中的信息加载到通讯录中。
LoadContact函数
void LoadContact(Contact* pc)
{
//读数据
//1.打开文件
FILE* pf = fopen("contact.txt", "rb");
if (NULL == pf)
{
perror("LoadContact");
}
else
{
//2.读数据
PeoInfo tmp = { 0 };
int i = 0;
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
check_capacity(pc);
pc->data[i] = tmp;
pc->sz++;
i++;
}
fclose(pf);
pf = NULL;
}
}
用FILE*类型的pf接收"contact.txt"的信息,用fopen函数选择“rb"(以二进制打开,读文件),打开如果能成功打开,再循环使用fread函数,读取文件,读取完后用fclose函数关闭文件,再将pf置为NULL,防止数据丢失。
(2)退出前,将信息保存到文件中
void SaveContact(Contact* pc)
{
//写数据
//1,打开文件
FILE* pf=fopen("contact.txt", "wb");
if (NULL == pf)
{
perror("SaveContact");
}
else
{
//写数据
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->data + i,sizeof(PeoInfo),1,pf);
}
fclose(pf);
pf = NULL;
}
printf("保存完成\n");
}
用FILE*类型的pf接收"contact.txt"的信息,用fopen函数选择“wb"(以二进制打开,写文件),打开如果能成功打开,再循环使用fwrite函数,将数据保存到文件中,保存完后用fclose函数关闭文件,再将pf置为NULL,防止数据丢失。
总结
本文只实现了通讯录的部分功能,从静态版本到动态版本,再到文件版本,一步步修改完善。我们在完成一些比较复杂的项目时,就该由易到难,将问题分解化、细化,再一步一步的解决,最后完成整个项目。也开动脑筋想想,看看哪些地方还可以优化,想想一个问题的多种解法,也可以将代码进行完善,实现更多功能。
源代码
contact.h
#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
#define DEFAULT_SZ 3
#define INC_SZ 2
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char addr[ADDR_MAX];
int tele[TELE_MAX];
}PeoInfo;
//静态版本
//typedef struct Contact
//{
// PeoInfo data[MAX];
// int sz;
//}Contact;
//动态版本
typedef struct Contact
{
PeoInfo* data;//指向存放人信息的空间
int sz;//当前已经放好信息的个数
int capacity;//当前通讯录最大容量
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//销毁通讯录
void DestroyContact(Contact* pc);
//显示通讯录的信息
void ShowContact(const Contact* pc);
//添加通讯录联系人
void AddContact(Contact* pc);
//删除指定联系人
void DelContact(Contact* pc);
//查找指定联系人
void SearchContact(const Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//清空所有联系人
void EmptyContact(Contact* pc);
//以名字排序所有联系人
void SortContact(Contact* pc);
//保存通讯录的信息到文件中
void SaveContact(Contact* pc);
//加载文件信息到通讯录
void LoadContact(Contact* pc);
contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//静态版本
//void InitContact(Contact* pc)
//{
// assert(pc);
// pc->sz = 0;
// memset(pc->data, 0, sizeof(pc->data));
//}
//动态版本
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
if (ptr == NULL)
{
perror("InitContact::calloc");
return;
}
pc->data = ptr;
pc->capacity = DEFAULT_SZ;
//加载文件信息到通讯录
LoadContact(pc);
}
//销毁通讯录
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
pc = NULL;
}
void ShowContact(const Contact* pc)
{
assert(pc);
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
//静态版本
//void AddContact(Contact* pc)
//{
// assert(pc);
// if (pc->sz == MAX)
// {
// 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].addr);
// printf("请输入电话:>");
// scanf("%s", pc->data[pc->sz].tele);
// pc->sz++;
//}
//增容
void check_capacity(Contact* pc)
{
if (pc->sz == pc->capacity)
{
//增加容量
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
if (ptr == NULL)
{
perror("check_capacity::realloc");
return;
}
pc->data = ptr;
pc->capacity += INC_SZ;
printf("增容成功\n");
}
}
//动态版本
void AddContact(Contact* pc)
{
assert(pc);
check_capacity(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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
int FindContact(const Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void DelContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
printf("请输入你想删除的联系人的姓名:>");
scanf("%s", name);
int ret = FindContact(pc, name);
if (-1 == ret)
{
printf("通讯录中没有此人\n");
return;
}
int i = 0;
for (i = ret; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
void SearchContact(const Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法查找\n");
return;
}
printf("请输入你想查找的联系人的姓名:>");
scanf("%s", name);
int pos = FindContact(pc, name);
if (-1 == pos)
{
printf("通讯录中没有此人\n");
return;
}
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].tele);
}
void ModifyContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法修改\n");
return;
}
printf("请输入你想修改的联系人的姓名:>");
scanf("%s", name);
int pos = FindContact(pc, name);
if (-1 == pos)
{
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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pos].tele);
printf("修改完成\n");
}
void EmptyContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
pc->capacity=0;//
printf("联系人已清空\n");
}
void SortContact(Contact* pc)
{
int i = 0;
int j = 0;
struct PeoInfo temp;
for (i = 0; i < pc->sz - 1; i++)
for (j = 0; j < pc->sz - 1 - i; j++)
{
if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
{
temp = pc->data[j];
pc->data[j] = pc->data[j + 1];
pc->data[j + 1] = temp;
}
}
printf("排序完成\n");
}
void SaveContact(Contact* pc)
{
//写数据
//1,打开文件
FILE* pf=fopen("contact.txt", "wb");
if (NULL == pf)
{
perror("SaveContact");
}
else
{
//写数据
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->data + i,sizeof(PeoInfo),1,pf);
}
fclose(pf);
pf = NULL;
}
printf("保存完成\n");
}
void LoadContact(Contact* pc)
{
//读数据
//1.打开文件
FILE* pf = fopen("contact.txt", "rb");
if (NULL == pf)
{
perror("LoadContact");
}
else
{
//2.读数据
PeoInfo tmp = { 0 };
int i = 0;
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
check_capacity(pc);
pc->data[i] = tmp;
pc->sz++;
i++;
}
fclose(pf);
pf = NULL;
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("************************************\n");
printf("****** 1. add 2. del ******\n");
printf("****** 3. search 4. modify ******\n");
printf("****** 5. show 6. empty ******\n");
printf("****** 7. sort 0. exit ******\n");
printf("************************************\n");
}
enum Option
{
EXIT,//0
ADD,//1
DEL,
SEACHER,
MODIFY,
SHOW,
EMPTY,
SORT,
};
int main()
{
int input = 0;
//创建通讯录
Contact con;
//初始化通讯录
InitContact(&con);
do
{
menu();
printf("请选择>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEACHER:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case EMPTY:
EmptyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
//保存通讯录信息到文件中
SaveContact(&con);
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
希望本文对你有所帮助,感谢阅读,愿与君共勉。