实现一个通讯录;
通讯录可以用来存储联系人人的信息,每个人的信息包括:
序号、姓名、性别、年龄、电话、住址
通讯录为用户提供的操作:
0.退出
1.添加联系人信息
2.删除指定联系人信息
3.查找指定联系人信息
4.修改指定联系人信息
5.显示所有联系人信息
6.清空所有联系人
7.以名字排序所有联系人
8.保存联系人到文件 9. 加载联系人
通过对通讯录的实现,复习的知识点有:结构体、数组、指针、动态内存开辟(malloc、calloc、realloc、free)、文件相关操作。
通讯录简介:
运行程序后,可以进入选择界面,通过输入菜单中的序号来选择相应的操作,如下图所示:
添加联系人信息,如下图所示(只展示添加一个联系人,其他的省略):
添加完联系人信息后输入5,显示所有联系人信息,如下图:
输入3查找联系人,如下图:
输入2删除指定的联系人,如下图:
其他功能便不在此一一展示了。
编写过程:
1.建立3个文件,分别是contact.c、contact.h、test.c
contact.c用来放一些自定义的函数;contact.h用于放函数声明等内容;test.c用于放主函数,组织整个程序框架
2.在test.c中写一个main()函数,作为程序运行的入口,将测试函数test()放到里面
int main()
{
test();
return 0;
}
3.写一个菜单函数,用于打印通讯录的菜单
void menu()
{
printf("**************************************************************\n");
printf("** 1. 添加联系人 2. 删除联系人 **\n");
printf("** 3. 查找联系人 4. 修改联系人信息 **\n");
printf("** 5. 显示所有联系人 6. 清空所有联系人 **\n");
printf("** 7. 联系人按姓名排序 0. 退出 **\n");
printf("**************************************************************\n");
}
4.我们定义一个结构体,命名为person_info,用来存储联系人的信息;定义一个结构体,命名为addres_book,用来存放动态分配的内存的地址、动态分配的内存的容量大小以及联系人的数量。
typedef struct person_info
{
int number;//为每个联系人分配一个编号
char name[NAME_MAX];
char sex[SEX_MAX];
short age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}person_info;
typedef struct address_book
{
person_info *data;
int count;//记录联系人个数
int capacity;//记录容量
}address_book;
5.在test.c中写一个测试函数test(),该函数用于将其他函数组织起来,成为完整的通讯录运行函数,同时也可以使main()函数看起来更简洁
void test()
{
int input = 1;
address_book book;
address_book_init(&book);//初始化通讯录
while (input)
{
menu();
printf("请选择>> ");
scanf("%d", &input);
switch (input)
{
case EXIT:
save_address_book(&book);
printf("退出成功!\n");
break;
case ADD:
address_book_add(&book);
break;
case DEL:
address_book_del(&book);
break;
case SEARCH:
address_book_find(&book);
break;
case MODIFY:
address_book_update(&book);
break;
case SHOW:
address_book_show(&book);
break;
case EMPTY:
address_book_del_all(&book);
break;
case SORT:
sort_by_name(&book);
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
}
}
6.子函数简介:
address_book_init( )函数
初始化通讯录。这个函数通过调用calloc( )函数动态分配三个起始空间,内容为空,并且通过调用address_book_load( )函数将文件中的数据加载进来。
address_book_add( )函数
添加联系人。当用户输入1后,test( )函数调用该函数,让用户添加一个联系人,这个函数会调用check_capacity( )函数自动扩容。
address_book_show( )函数
显示所有联系人信息 。当用户输入5后,test( )函数调用该函数,将通讯录中所有的联系人信息打印出来。
address_book_del_all( )函数
清空所有联系人。当用户输入6后,test( )函数调用该函数,该函数将释放之前动态分配的所有空间,并将联系人计数变量和内存计数变量都清空。
address_book_del( )函数
删除指定联系人信息 。当用户输入2后,test( )函数调用该函数,该函数将会提示用户输入要删除的联系人的姓名,当用户输入姓名后,该函数会调用Find( )函数进行查找,(考虑到用户有可能会将两个同名的联系人存到通讯录中)然后将找到的所有同名的联系人的相关信息打印出来。此时该函数会提示用户输入要删除的联系人的序号,这样就可以避免同名的联系人的信息误删的情况了。
address_book_find( )函数
查找指定联系人信息,并将其打印。当用户输入3后,test( )函数将调用该函数,该函数会提示用户输入要查找的联系人的姓名,当用户输入姓名后,该函数将调用Find( )函数进行查找,并将找到的所有同名联系人信息打印出来。
address_book_update( )函数
修改指定联系人信息。当用户输入4后,test( )函数将调用该函数,该函数会提示用户输入要修改的用户的姓名,然后通过用户输入信息对原变量重新赋值,达到修改信息的目的。
sort_by_name( )函数
将所有的联系人按名字排序。当用户输入7后,test( )函数将调用该函数,该函数会通过使用qsort( )函数对联系人信息(person_info类型的结构体变量)按姓名进行排序。
save_address_book( )函数
将通讯录的内容存入文件中。当用户输入0打算退出的时候,test( )函数将调用该函数,该函数通过文件二进制写操作将person_info类型的数组中的数据(联系人信息)保存到address book.data文件中。
check_capacity( )函数
通过联系人的数量(count)和动态分配的内存的容量(capacity)的关系判断是否需要进行扩容,扩容的方法是使用realloc( )函数重新分配一块更大的内存空间。
address_book_load( )函数
将address book.data文件中的数据加载到动态分配的数组中,如果内存大小不够,该函数会通过调用check_capacity( )函数对内存进行扩充。该函数被address_book_init( )函数调用 ,用于用户打开通讯录程序的时候的数据加载。
Find( )函数
查找联系人并返回该联系人的位置,找不到返回-1.该函数被address_book_del( )、sort_by_name()和address_book_update( )调用,用来通过姓名查找联系人。
通讯录代码总览:
contact.h
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#define NAME_MAX 11
#define SEX_MAX 5
#define TEL_MAX 12
#define ADDR_MAX 21
#define DEFAULT_SIZE 3
enum//将菜单定义为一个枚举类
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
EMPTY,
SORT
};
typedef struct person_info
{
int number;//为每个联系人分配一个编号
char name[NAME_MAX];
char sex[SEX_MAX];
short age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}person_info;
typedef struct address_book
{
person_info *data;
int count;//记录联系人个数
int capacity;//记录容量
}address_book;
void address_book_init(address_book *);//初始化通讯录
void address_book_add(address_book *);//添加联系人
void address_book_show(const address_book *);//显示所有联系人信息
void address_book_del_all(address_book *);//清空所有联系人
void address_book_del(address_book *);// 删除指定联系人信息
void address_book_find(address_book *);//查找指定联系人信息,并将其打印
void address_book_update(address_book *);//修改指定联系人信息
void sort_by_name(address_book *);//将所有的联系人按名字排序
void save_address_book(address_book *);//将通讯录的内容存入文件中
contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void check_capacity(address_book *pbook)
{
assert(pbook);
if (pbook->capacity == pbook->count)//如果满了则扩容
{
person_info *ptmp = (person_info *)realloc(pbook->data, (pbook->capacity+2)*sizeof(person_info));
if (ptmp != NULL)
{
pbook->capacity += 2;
pbook->data = ptmp;
//printf("扩容成功!\n");
}
}
}
void address_book_load(address_book *pbook)
{
person_info tmp = {0};
FILE *pfread = fopen("address book.data", "rb");
if (pfread == NULL)
{
printf("加载失败:打开文件失败!\n");
return;
}
//加载消息
while (fread(&tmp, sizeof(person_info), 1, pfread))
{
check_capacity(pbook);
pbook->data[pbook->count] = tmp;
pbook->count++;
}
fclose(pfread);
pfread = NULL;
}
void address_book_init(address_book *pbook)
{
pbook->count = 0;
//memset(pbook->data, '0', sizeof(pbook->data));
pbook->data = (person_info *)calloc(DEFAULT_SIZE, sizeof(person_info));
if (pbook->data == NULL)
{
printf("%s\n", strerror(errno));
return;
}
pbook->capacity = DEFAULT_SIZE;
address_book_load(pbook);//加载通讯录
}
void address_book_show(const address_book *pbook)
{
int i = 0;
if (pbook->count == 0)
{
printf("通讯录内容为空,无联系人可显示!\n");
return;
}
printf("%-8s%-14s%-8s%-7s%-15s%-24s\n", "序号","姓名", "性别", "年龄", "电话", "地址");
for (i = 0; i < (pbook->count); i++)
{
printf("%-8d%-14s%-8s%-7d%-15s%-24s\n",
pbook->data[i].number,
pbook->data[i].name,
pbook->data[i].sex,
pbook->data[i].age,
pbook->data[i].tel,
pbook->data[i].addr);
}
}
void address_book_add(address_book *pbook)
{
assert(pbook);
check_capacity(pbook);
printf("请输入要添加的联系人的信息!\n");
printf("姓名>> ");
scanf("%s", pbook->data[pbook->count].name);
printf("性别>> ");
scanf("%s", pbook->data[pbook->count].sex);
printf("年龄>> ");
scanf("%d", &(pbook->data[pbook->count].age));
printf("电话>> ");
scanf("%s", pbook->data[pbook->count].tel);
printf("地址>> ");
scanf("%s", pbook->data[pbook->count].addr);
pbook->data[pbook->count].number = pbook->count + 1;
(pbook->count)++;
printf("添加成功!\n");
}
void address_book_del_all(address_book *pbook)
{
free(pbook->data);
pbook->data = NULL;
pbook->capacity = 0;
pbook->count = 0;
printf("通讯录内容已经被清空!\n");
}
static int Find(const address_book *pbook, char *name, int i)//查找联系人并返回该联系人的位置,找不到返回-1
{
for (; i < (pbook->count); i++)//i表示要查找的位置
{
if (strcmp((pbook->data[i]).name, name) == 0)
{
return i;
}
}
return -1;
}
void address_book_del(address_book *pbook)
{
int i = 0;
int tmp = 0;
int tag1 = 0;
int tag2 = 0;
char name[11] = {0};
person_info empty = {0};
if (0 == (pbook->count))
{
printf("通讯录为空,没有内容可删除!\n");
return;
}
printf("请输入要删除的联系人的姓名>> ");
scanf("%s", name);
while (tmp < (pbook->count))
{
tmp = Find(pbook, name, tag2);
if (-1 != tmp)
{
tag2 = tmp + 1;
if (0 == tag1)
{
printf("%-8s%-14s%-8s%-7s%-15s%-24s\n",
"序号","姓名", "性别", "年龄", "电话", "地址");
}
printf("%-8d%-14s%-8s%-7d%-15s%-24s\n",
pbook->data[tmp].number,
pbook->data[tmp].name,
pbook->data[tmp].sex,
pbook->data[tmp].age,
pbook->data[tmp].tel,
pbook->data[tmp].addr);
tag1 = 1;
}
else
{
break;
}
}
if (0 == tag1)
{
printf("找不到联系人:%s!\n", name);
}
else
{
while (1)
{
printf("请输入要删除的联系人的序号>> ");
scanf("%d", &i);
if (strcmp(pbook->data[i-1].name, name) != 0)
{
printf("输入的序号有误,请重新输入!\n");
continue;
}
else//输入的序号与输入的姓名对应才会删除
{
for (;i < pbook->count; i++)
{
pbook->data[i-1] = pbook->data[i];
pbook->data[i-1].number = i;//修正序号
}
//memset(&(pbook->data[(pbook->count) - 1]), '0', sizeof(person_info));//最后一个单元清空
pbook->data[(pbook->count) - 1] = empty;
printf("删除成功!\n");
(pbook->count)--;
break;
}
}
}
}
void address_book_find(address_book *pbook)
{
int tmp = 0;
int tag1 = 0;//标记是否找到
int tag2 = 0;//标记查找的位置
char name[11] = {0};
if (0 == (pbook->count))
{
printf("通讯录为空,无内容可查找!\n");
return;
}
printf("请输入要查找的联系人的姓名>> ");
scanf("%s", name);
while (tag2 < (pbook->count))
{
tmp = Find(pbook, name, tag2);
if (-1 != tmp)
{
tag2 = tmp + 1;//如果找到了就更新tag2
if (0 == tag1)
{
printf("%-8s%-14s%-8s%-7s%-15s%-24s\n",
"序号","姓名", "性别", "年龄", "电话", "地址");
}
printf("%-8d%-14s%-8s%-7d%-15s%-24s\n",
pbook->data[tmp].number,
pbook->data[tmp].name,
pbook->data[tmp].sex,
pbook->data[tmp].age,
pbook->data[tmp].tel,
pbook->data[tmp].addr);
tag1 = 1;
}
else
{
break;
}
}
if (0 == tag1)
{
printf("找不到联系人:%s!\n", name);
}
}
void address_book_update(address_book *pbook)
{
int i = 0;
int tmp = 0;
char name[11] = {0};
if (0 == pbook->count)
{
printf("通讯录内容为空,无法修改联系人!\n");
return;
}
printf("请输入要修改的联系人的姓名>> ");
scanf("%s", name);
tmp = Find(pbook, name, i);
if (-1 != tmp)
{
printf("姓名修改为>> ");
scanf("%s", pbook->data[tmp].name);
printf("性别修改为>> ");
scanf("%s", pbook->data[tmp].sex);
printf("年龄修改为>> ");
scanf("%d", &(pbook->data[tmp].age));//这里需要注意一下:记得取地址
printf("电话修改为>> ");
scanf("%s", pbook->data[tmp].tel);
printf("地址修改为>> ");
scanf("%s", pbook->data[tmp].addr);
printf("修改成功!\n");
}
else
{
printf("找不到联系人:%s!\n", name);
}
}
static int cmp_by_name(const void *p1, const void *p2)
{
return strcmp(((person_info *)p1)->name, ((person_info *)p2)->name);
}
void sort_by_name(address_book *pbook)//使用qsort函数进行排序
{
if (pbook->count == 0)
{
printf("通讯录的内容为空,不需要排序!\n");
return;
}
qsort(pbook->data, (pbook->count), sizeof(pbook->data[0]), cmp_by_name);//需要排序的联系人个数是count个
printf("排序成功\n");
}
void save_address_book(address_book *pbook)
{
int i = 0;
FILE *pfwrite = fopen("address book.data", "wb");
if (pfwrite == NULL)
{
printf("保存失败:打开文件失败!\n");
return;
}
for (i = 0; i < pbook->count; i++)
{
fwrite(pbook->data+i, sizeof(person_info), 1, pfwrite);
}
fclose(pfwrite);
pfwrite = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void menu()
{
printf("**************************************************************\n");
printf("** 1. 添加联系人 2. 删除联系人 **\n");
printf("** 3. 查找联系人 4. 修改联系人信息 **\n");
printf("** 5. 显示所有联系人 6. 清空所有联系人 **\n");
printf("** 7. 联系人按姓名排序 0. 退出 **\n");
printf("**************************************************************\n");
}
void test()
{
int input = 1;
address_book book;
address_book_init(&book);//初始化通讯录
while (input)
{
menu();
printf("请选择>> ");
scanf("%d", &input);
switch (input)
{
case EXIT:
save_address_book(&book);
printf("退出成功!\n");
break;
case ADD:
address_book_add(&book);
break;
case DEL:
address_book_del(&book);
break;
case SEARCH:
address_book_find(&book);
break;
case MODIFY:
address_book_update(&book);
break;
case SHOW:
address_book_show(&book);
break;
case EMPTY:
address_book_del_all(&book);
break;
case SORT:
sort_by_name(&book);
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
}
}
int main()
{
test();
return 0;
}