基于顺序表实现的通讯录
- 通讯录的基本功能
- 顺序表
- 顺序表的部分变量修改
- 修改处一
- 修改处二
- 修改处三
- 头文件 Contact.h
- 通讯录自定义结构体
- 功能实现 源文件 Contact.c
- 读取文件中联系人的信息 void ContactReadFile(contact* pcon)
- 保存到文件 void ContactSave(contact* pcon)
- 测试文件的保存与读取
- 初始化 void ContactInit(contact* pcon)
- 销毁当前联系人信息 void ContactDestroy(contact* pcon)
- 添加联系人 void ContactAdd(contact* pcon)
- 删除联系人 void ContactDel(contact* pcon)
- 展示单个人物信息 void ContactSpecifyShow(contact* pcon, unsigned int pos)
- 展示所有联系人信息 void ContactShow(contact* pcon)
- 测试添加与删除联系人
- 查找联系人 void ContactFind(contact* pcon)
- 更改通讯录信息 void ContactChange(contact* pcon)
- 测试查找与更改联系人信息
- 逻辑实现 源文件 test.c
- 界面菜单 void menu(void)
- 主体逻辑 void test_contact(void)
- 结语
- 源代码
- Contact.c
- test.c
- SeqList.h
- SeqList.h
通讯录的基本功能
通讯录是用来存储联系人的基本信息,同时也具备对联系人基本信息的添加,删除,修改,查询,显示信息的功能,可以辅助使用者在日常生活中的信息记忆等等。
而存储的信息我们也可以根据自己的需求来设计,这里我们就拿联系人的基本信息:姓名,电话号码,爱好,因她而存在的故事 来举例。
顺序表
这里我们要使用上一期所讲到的顺列表来辅助实现,(不懂的友友们可以先看看上一期顺序表的实现)链接:顺序表的实现。
注意:一定要了解上期顺序表的实现,因为本期通讯录是基于上期顺序表所实现的。
顺序表的部分变量修改
因为通讯录是使用结构体类型,而我们上一期所用到的是 整型变量 所测试的,所以我们在这里要进行部分变量的修改,修改如下:
修改处一
//SeqList.h
#define SLDataType CInfo
bool SLFind(SL* ps, char* x);
int SLFindPos(SL* ps, char* x);
这里将之前的int
改为CInfo
,CInfo
是后续我们要实现的结构体,并将SLFind函数与SLFindPos函数
的第二个参数的变量改为char*
类型的变量,因为我们通讯录使用结构体为数组元素单位,后续也要用char*
类型来存储名字,所以需要改变。
修改处二
//SeqList.c
需要修改的两个函数如下:
在查找名字的时候,我们传的是字符串指针,需要断言,并且利用strcmp(str1,str2)
库函数来比较,两个字符串是否相同,相同则返回0,不同则返回非0。
修改处三
//SeqList.c
如下图:
之前用到的打印顺序表元素的函数现阶段也不适用于结构体,所以要注释掉,之后我们会重新编写通讯录的打印函数。
这也是为了后续查找联系人做准备。
头文件 Contact.h
#pragma once
#define NAME_MAX 20
#define PHONE_NUMBER_MAX 30
#define HOBBY_MAX 500
#define STORY_MAX 3000
typedef struct ContactInfo
{
char name[NAME_MAX];
char Phone_number[PHONE_NUMBER_MAX];
char hobby[HOBBY_MAX];
char Story[STORY_MAX];
}CInfo;
typedef struct SeqList contact;
void ContactReadFile(contact* pcon);//读取文件中联系人的信息
void ContactInit(contact* pcon);//初始化
void ContactDestroy(contact* pcon);//销毁
void ContactShow(contact* pcon);//展示所有联系人信息
void ContactAdd(contact* pcon);//添加联系人
void ContactDel(contact* pcon);//删除联系人
void ContactFind(contact* pcon);//查找联系人
void ContactSpecifyShow(contact* pcon, unsigned int pos);//展示单个人物信息
void ContactSave(contact* pcon);//保存到文件
void ContactChange(contact* pcon);//更改通讯录信息
因为这里要实现的是通讯录,所以我们需要的顺列表的结构体重命名:typedef struct SeqList contact;
,这是因为修改后写成Contact
更符合整体风格也更方便阅读。
通讯录自定义结构体
#define NAME_MAX 20
#define PHONE_NUMBER_MAX 30
#define HOBBY_MAX 500
#define STORY_MAX 3000
typedef struct ContactInfo
{
char name[NAME_MAX];
char Phone_number[PHONE_NUMBER_MAX];
char hobby[HOBBY_MAX];
char Story[STORY_MAX];
}CInfo;
为了满足多样化的输入,我这里使用都是字符类型的数组,其依次表示:姓名,电话号码,爱好,因她而存在的故事 ,这里数组的空间大小我们也用#define 宏定义
修改方便后续修改。
功能实现 源文件 Contact.c
读取文件中联系人的信息 void ContactReadFile(contact* pcon)
void ContactReadFile(contact* pcon)
{
CInfo s = { 0 };
FILE* pf = fopen("Contact.txt", "r");
if (pf == NULL)
{
FILE* pw = fopen("Contact.txt", "w+");
assert(pw);
fprintf(pw, "%d\n", 0);
fclose(pw);
pw = NULL;
pf = fopen("Contact.txt", "r");
}
int i = 0;
fscanf(pf, "%d", &i);
while (i--)
{
fscanf(pf, "%s %s %s %s", &(s.name), &(s.Phone_number), &(s.hobby), &(s.Story));
SLPushBack(pcon, s);
}
fclose(pf);
pf = NULL;
}
FILE* pf = fopen("Contact.txt", "r");
以只读模式创建一个文件指针来读取"Contact.txt"
,当然在第一次使用的时候,我们的程序文件夹在电脑磁盘中是没有这个文件的,所以fopen
会返回一个空指针NULL,所以我们要对pf
进行一个判断,若pf
为空,我们就要创建一个文件指针pw
用"w+"读写模式创建一个文本文件,然后再用pf
重新读取。
目前,我们已经成功打开了**“Contact.txt”,我们现在要开始读取文件,如何读取文件呢?这里我们使用了fscanf()
函数来进行读取,每次同时读取联系人的名字、电话号、爱好,故事**,再利用while循环来限制读取次数。
那么这个限制次数是多少呢?我们在第一次创建文件时,细节的输入了一个0
,这就说明了这个文件中并没有一个联系人的数据,但是如果说我们不是第一次使用,那限制次数又该如何计算呢?
int i = 0;
fscanf(pf, "%d", &i);
每当我们进入while循环中,限制次数就是我们在文件第一行读取到的数字,而保存这个数的任务,这就要看我们的下一个函数了。
保存到文件 void ContactSave(contact* pcon)
void ContactSave(contact* pcon)
{
FILE* pw = fopen("Contact.txt", "w");
assert(pw);
fprintf(pw, "%d\n", pcon->size);
for (int i = 0; i < pcon->size; i++)
{
fprintf(pw, "%s\n%s\n%s\n%s\n",
pcon->arr[i].name, pcon->arr[i].Phone_number,
pcon->arr[i].hobby, pcon->arr[i].Story);
}
fclose(pw);
pw = NULL;
}
fprintf(pw, "%d\n", pcon->size);
在保存文件时,我们第一行保存通讯录的有效联系人个数,这也是方便ContactSave
函数保存。
fprintf(pw, "%s\n%s\n%s\n%s\n",
pcon->arr[i].name, pcon->arr[i].Phone_number,
pcon->arr[i].hobby, pcon->arr[i].Story);
利用for循环将数位联系人的信息保存在文件中,然后再关闭文件即可。
测试文件的保存与读取
目前,我们存放程序的文件夹是没有"Contact.txt"
文本文件的,下面让我们运行来看一下吧。
我们可以看到在文件夹中多出了一个"Contact.txt"
文本文件,并且通过文件预览我们也可看到程序成功将信息保存在了文本中,程序的返回值也为0,所以可以说明代码没问题。
初始化 void ContactInit(contact* pcon)
void ContactInit(contact* pcon)
{
SLInit(pcon);
ContactReadFile(pcon);
}
基于以上代码,这里通讯录初始化的实现,只要先调用SLInit(pcon)函数让顺序表初始化,再调用ContactReadFile(pcon)函数读取磁盘中所存储文件即可。
销毁当前联系人信息 void ContactDestroy(contact* pcon)
void ContactDestroy(contact* pcon)
{
SLDestroy(pcon);
}
同理,销毁当前联系人信息就相当于销毁顺序表。
添加联系人 void ContactAdd(contact* pcon)
void ContactAdd(contact* pcon)
{
CInfo info;
printf("请输入姓名:");
scanf("%s", info.name);
printf("请输入电话号码:");
scanf("%s", &info.Phone_number);
printf("请输入爱好:");
scanf("%s", info.hobby);
printf("请输入与他一起的趣事:");
scanf("%s", info.Story);
SLPushBack(pcon, info);
}
先创建一个临时变量info
,通过scanf
函数来输入数据,再将info
尾插到顺序表即可实现。
删除联系人 void ContactDel(contact* pcon)
void ContactDel(contact* pcon)
{
assert(pcon);
printf("输入你要删除的联系人:");
char x[NAME_MAX] = { 0 };
scanf("%s", x);
int ret = SLFindPos(pcon, x);
if (ret >= 0)
{
SLErase(pcon, ret);
printf("删除成功!\n");
}
else
{
printf("不存在该联系人\n");
}
}
先利用scanf函数输入联系人姓名的信息x
,再利用顺序表中的SLFindPos(pcon, x)函数来查找姓名的位置,若返回大于0
的值说明x
存在,此时就用SLErase(pcon, ret)函数再顺序表中进行删除;若为-1
,这说明该联系人x
根本不存在。
展示单个人物信息 void ContactSpecifyShow(contact* pcon, unsigned int pos)
void ContactSpecifyShow(contact* pcon, unsigned int pos)
{
printf("\n姓名:%s\n电话号码:%s\n爱好:%s\nThe story that exists because of her:\n%s\n",
pcon->arr[pos].name,
pcon->arr[pos].Phone_number,
pcon->arr[pos].hobby,
pcon->arr[pos].Story
);
}
通过传入顺序表的地址与数组下标来确定联系人的位置,再依次打印数据即可。
PS:在展示联系人与使用者所产生的故事的时候,考虑到字数可能较多影响美观,所以这里在he story that exists because of her:
后面进行了换行。
展示所有联系人信息 void ContactShow(contact* pcon)
void ContactShow(contact* pcon)
{
for (int i = 0; i < pcon->size; i++)
{
ContactSpecifyShow(pcon, i);
}
}
利用for循环遍历顺序表利用ContactSpecifyShow(pcon, i)函数进行逐个打印。
测试添加与删除联系人
#include"SeqList.h"
//测试添加与删除联系人
int main()
{
contact con;
ContactInit(&con);
printf("输入_1:\n");
ContactAdd(&con);
printf("\n输入_2:\n");
ContactAdd(&con);
ContactShow(&con);
printf("\n删除_1\n");
ContactDel(&con);
ContactShow(&con);
ContactDestroy(&con);
return 0;
}
(PS:为了更清晰的表达本次实验,这里删除了上一次测试所保留的"Contact.txt"
文件)
先进行两次输入,然后展示通讯录,再删除原神,再展示通讯录。运行结果如下:
查找联系人 void ContactFind(contact* pcon)
void ContactFind(contact* pcon)
{
assert(pcon);
printf("输入你要查找的联系人:");
char x[NAME_MAX] = { 0 };
scanf("%s", x);
int ret = SLFindPos(pcon, x);
if (ret >= 0)
{
ContactSpecifyShow(pcon, ret);
}
else
{
printf("不存在该联系人\n");
}
}
这里也是基于顺序表,先判断输入的名字x
是否存在,存在则单独打印该联系人信息,反之告知不存在。
更改通讯录信息 void ContactChange(contact* pcon)
void ContactChange(contact* pcon)
{
char str[NAME_MAX] = { 0 };
printf("输入你要更改的该联系人:");
scanf("%s", str);
int ret = SLFindPos(pcon , str);
if (ret < 0)
{
printf("不存在该联系人");
return 0;
}
CInfo info;
printf("请输入更正后的姓名:");
scanf("%s", info.name);
printf("请输入更正后的电话号码:");
scanf("%s", &info.Phone_number);
printf("请输入更正后的爱好:");
scanf("%s", info.hobby);
printf("请输入更正后的故事:");
scanf("%s", info.Story);
SLErase(pcon, ret);
SLInsert(pcon, ret, info);
}
基于顺序表SLFindPos(pcon , str)函数,先判断要更改人的姓名,是否存在于通讯录,若存在,则让用户输入修改信息,然后先调用SLErase(pcon, ret)函数将该位置的原数据删除,再调用SLInsert(pcon, ret, info)函数对该位置进行添加;反之告知不存在联系人并退出函数。
测试查找与更改联系人信息
先添加三个联系人a,b,c
,再查找已存在的a
与·不存在的d
,再更改联系人a
为d
,再查找a
与d
,根据逻辑程序运行结果符合预期。
逻辑实现 源文件 test.c
界面菜单 void menu(void)
void menu()
{
printf("+-------------------------------------------------------+\n");
printf("| 欢迎来到月下的依靠-----志昂的通讯录 |\n");
printf("| 请选择你的操作: |\n");
printf("| 1.ContactInit 初始化备忘录 |\n");
printf("| 2.ContactAdd 添加联系人信息 |\n");
printf("| 3.ContactDel 删除联系人信息 |\n");
printf("| 4.ContactShow 展示联系人信息 |\n");
printf("| 5.ContactFind 查找联系人信息 |\n");
printf("| 6.ContactSave 保存至文件中 |\n");
printf("| 7.ContactChange 更改联系人信息 |\n");
printf("| 8.ContactDestroy 销毁当前联系人信息 |\n");
printf("| 9.ClearScreen 清除屏幕 |\n");
printf("| 0.CloseProgram 退出程序 |\n");
printf("+-------------------------------------------------------+\n");
}
这里的菜单只要表达的内容可以让使用者知道如何操作即可。
主体逻辑 void test_contact(void)
void test_contact()
{
contact con;
ContactInit(&con);
void (*p[9])(contact * pcon) = { 0,ContactInit, ContactAdd ,
ContactDel, ContactShow ,ContactFind, ContactSave,
ContactChange ,ContactDestroy};
menu();
int choose = 0;
printf("请输入指令:");
while (scanf("%d", &choose), choose != 0)
{
switch (choose)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
(*p[choose])(&con);
break;
case 9:
system("cls");
menu();
break;
default:
printf("请正确输入!!!!\n");
break;
}
printf("请输入指令:");
}
ContactDestroy(&con);
}
void (*p[9])(contact * pcon) = { 0,ContactInit, ContactAdd ,
ContactDel, ContactShow ,ContactFind, ContactSave, ContactChange ,ContactDestroy};
利用函数指针数组存放函数指针,使得输入的数字可以轻松调用函数地址来使用。
当输入值为1~8
时,进入到函数指针数组进行调用;
当输入9
就利用system()
调用控制台指令"cls"
来清空屏幕,并重新打印菜单;
当输入为0
时,break
跳出循环并销毁顺序表所存储的通讯录;
否则就要求使用者重新输入。
结语
到此基于顺序表和文件操作所实现的通讯录就实现了,本期的内容到这里就全部结束了,谢谢大家的观看。
那么,喜欢请多多关注吧。
画师:.com
投稿时间:2023年02月03日23:01
作品lD: 105059867
画师ID: 3889453
源代码
所有源代码如下:
Contact.c
#include"SeqList.h"
void ContactReadFile(contact* pcon)
{
CInfo s = { 0 };
FILE* pf = fopen("Contact.txt", "r");
if (pf == NULL)
{
FILE* pw = fopen("Contact.txt", "w+");
assert(pw);
fprintf(pw, "%d\n", 0);
fclose(pw);
pw = NULL;
pf = fopen("Contact.txt", "r");
}
//读文件
int i = 0;
fscanf(pf, "%d", &i);
while (i--)
{
fscanf(pf, "%s %s %s %s", &(s.name), &(s.Phone_number), &(s.hobby), &(s.Story));
SLPushBack(pcon, s);
}
fclose(pf);
pf = NULL;
}
void ContactSpecifyShow(contact* pcon, unsigned int pos)
{
printf("\n姓名:%s\n电话号码:%s\n爱好:%s\nThe story that exists because of her:\n%s\n",
pcon->arr[pos].name,
pcon->arr[pos].Phone_number,
pcon->arr[pos].hobby,
pcon->arr[pos].Story
);
}
void ContactInit(contact* pcon)
{
SLInit(pcon);
ContactReadFile(pcon);
}
void ContactDestroy(contact* pcon)
{
SLDestroy(pcon);
}
void ContactAdd(contact* pcon)
{
CInfo info;
printf("请输入姓名:");
scanf("%s", info.name);
printf("请输入电话号码:");
scanf("%s", &info.Phone_number);
printf("请输入爱好:");
scanf("%s", info.hobby);
printf("请输入与他一起的趣事:");
scanf("%s", info.Story);
SLPushBack(pcon, info);
}
void ContactShow(contact* pcon)
{
for (int i = 0; i < pcon->size; i++)
{
ContactSpecifyShow(pcon, i);
}
}
void ContactFind(contact* pcon)
{
assert(pcon);
printf("输入你要查找的联系人:");
char x[NAME_MAX] = { 0 };
scanf("%s", x);
int ret = SLFindPos(pcon, x);
if (ret >= 0)
{
ContactSpecifyShow(pcon, ret);
}
else
{
printf("不存在该联系人\n");
}
}
void ContactDel(contact* pcon)
{
assert(pcon);
printf("输入你要删除的联系人:");
char x[NAME_MAX] = { 0 };
scanf("%s", x);
int ret = SLFindPos(pcon, x);
if (ret >= 0)
{
SLErase(pcon, ret);
printf("删除成功!\n");
}
else
{
printf("不存在该联系人\n");
}
}
void ContactSave(contact* pcon)
{
FILE* pw = fopen("Contact.txt", "w");
assert(pw);
fprintf(pw, "%d\n", pcon->size);
for (int i = 0; i < pcon->size; i++)
{
fprintf(pw, "%s\n%s\n%s\n%s\n",
pcon->arr[i].name, pcon->arr[i].Phone_number,
pcon->arr[i].hobby, pcon->arr[i].Story);
}
fclose(pw);
pw = NULL;
}
void ContactChange(contact* pcon)
{
char str[NAME_MAX] = { 0 };
printf("输入你要更改的该联系人:");
scanf("%s", str);
int ret = SLFindPos(pcon , str);
if (ret < 0)
{
printf("不存在该联系人");
return 0;
}
CInfo info;
printf("请输入更正后的姓名:");
scanf("%s", info.name);
printf("请输入更正后的电话号码:");
scanf("%s", &info.Phone_number);
printf("请输入更正后的爱好:");
scanf("%s", info.hobby);
printf("请输入更正后的故事:");
scanf("%s", info.Story);
SLErase(pcon, ret);
SLInsert(pcon, ret, info);
}
test.c
#include"SeqList.h"
void menu()
{
printf("+-------------------------------------------------------+\n");
printf("| 欢迎来到月下的依靠-----志昂的通讯录 |\n");
printf("| 请选择你的操作: |\n");
printf("| 1.ContactInit 初始化备忘录 |\n");
printf("| 2.ContactAdd 添加联系人信息 |\n");
printf("| 3.ContactDel 删除联系人信息 |\n");
printf("| 4.ContactShow 展示联系人信息 |\n");
printf("| 5.ContactFind 查找联系人信息 |\n");
printf("| 6.ContactSave 保存至文件中 |\n");
printf("| 7.ContactChange 更改联系人信息 |\n");
printf("| 8.ContactDestroy 销毁当前联系人信息 |\n");
printf("| 9.ClearScreen 清除屏幕 |\n");
printf("| 0.CloseProgram 退出程序 |\n");
printf("+-------------------------------------------------------+\n");
}
void test_contact()
{
contact con;
ContactInit(&con);
void (*p[9])(contact * pcon) = { 0,ContactInit, ContactAdd ,
ContactDel, ContactShow ,ContactFind, ContactSave,
ContactChange ,ContactDestroy};
menu();
int choose = 0;
printf("请输入指令:");
while (scanf("%d", &choose), choose != 0)
{
switch (choose)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
(*p[choose])(&con);
break;
case 9:
system("cls");
menu();
break;
default:
printf("请正确输入!!!!\n");
break;
}
printf("请输入指令:");
}
ContactDestroy(&con);
}
int main()
{
test_contact();
//ContactReadFile();
//test_1();
return 0;
}
SeqList.h
#include"SeqList.h"
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLDestroy(SL* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLCheckCapacity(SL* ps)
{
if (ps->capacity == ps->size)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
assert(tmp);
ps->arr = tmp;
tmp = NULL;
ps->capacity = newcapacity;
}
}
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
int i = 0;
for (i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->size++;
ps->arr[0] = x;
}
//void SLPrint(SL* ps)
//{
// int i = 0;
// for (i = 0; i < ps->size; i++)
// {
// printf("%d ", ps->arr[i]);
// }
// printf("\n");
//}
void SLPopBack(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));
ps->size--;
}
void SLPopFront(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));
int i = 0;
for (i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
bool SLIsEmpty(SL* ps)
{
assert(ps);
return (ps->size == 0);
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
if (pos < 0 || pos > ps->size)
{
printf("输入错误!!!");
return;
}
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(!SLIsEmpty(ps));
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
bool SLFind(SL* ps, char* x)
{
assert(ps&&x);
for (int i = 0; i < ps->size; i++)
{
if(0 == strcmp(ps->arr[i].name,x))
return true;
}
return false;
}
int SLFindPos(SL* ps, char* x)
{
assert(ps&&x);
for (int i = 0; i < ps->size; i++)
{
if(0 == strcmp(ps->arr[i].name, x))
return i;
}
return -1;
}
void SLRevise(SL* ps, int pos, SLDataType x)
{
assert(ps);
if (pos >= ps->size && ps < 0)
{
printf("输入错误,无法修改\n");
return;
}
ps->arr[pos] = x;
}
SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include"Contact.h"
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#define SLDataType CInfo
typedef struct SeqList
{
SLDataType* arr;
int size;
int capacity;
}SL;
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
void SLPopBack(SL* ps);
void SLErase(SL* ps, int pos);
void SLInsert(SL* ps, int pos, SLDataType x);
bool SLFind(SL* ps, char* x);
int SLFindPos(SL* ps, char* x);
void SLRevise(SL* ps, int pos, SLDataType x);
//void SLPrint(SL* ps);
bool SLIsEmpty(SL* ps);