❀❀❀ 文章由@不准备秃的大伟原创 ❀❀❀
♪♪♪ 若有转载,请联系博主哦~ ♪♪♪
❤❤❤ 致力学好编程的宝藏博主,代码兴国!❤❤❤
欢迎回来,铁汁们!上一篇文章我们用C语言手撕了一个顺序表,然后就会有很多铁汁们都想知道这个数据结构我们学了有神么作用,诶,那今天这篇文章就给大家简单的了解一下我们数据结构的一些实际的作用!
什么?你已经忘了顺序表是如何实现的?ˋ( ° ▽、° ) (o( ̄▽ ̄///(斩!!) 那还不赶紧给博主来波一键三连解锁上一篇的顺序表---------> 顺序表
通讯录(顺序表版)
好的,废话不多说,我们先来分析一下通讯录该有什么功能呢?当然了,是个人都会知道:添加联系人,删除联系人,修改联系人,查找联系人,展示联系人,当然,还有退出通讯录啦 ( ̄▽ ̄)~■干杯□~( ̄▽ ̄)
那我们该如何开始写我们的通讯录呢?诶,既然是顺序表版,那么就把顺序表的内容抄过来呗,其他的后面再说!
开始写通讯录部分,作为前期,我们还是和顺序表一样,开三个文件,分别为Contact.c , Contact.c 和 test.c 文件,Contact.c文件实现功能,Contact.h文件声明函数和定义头文件,test.c文件测试我们的通讯录功能。
同顺序表一样,先定义个结构体,放联系人的各个信息。当然了,因为我们的名字啊,性别等都是字符串,所以我们不如定义几个宏,限制各个字符串的最大长度,如下:
#define NAME_MAX 10
#define SEX_MAX 6
#define TEL_MAX 12
#define ADDRE_MAX 20
typedef struct PersonInfo
{
char name[NAME_MAX];
char gender[SEX_MAX];
int age;
char tel[TEL_MAX];
char addre[ADDRE_MAX];
}PersonInfo;
然后就是我们的通讯录的各个功能的声明,如下:
//初始化通讯录
void InitContact(Contact* con);
//添加通讯录数据
void AddContact(Contact* con);
//删除通讯录数据
void DelContact(Contact* con);
//展⽰通讯录数据
void ShowContact(Contact* con);
//查找通讯录数据
void FindContact(Contact* con);
//修改通讯录数据
void ModifyContact(Contact* con);
//销毁通讯录数据
void DestroyContact(Contact* con);
接着我们就寻思啊,我们的顺序表的内容存放的都是int类型的,那我们将顺序表指向这个PersonInfo的结构体可以吗? 答案是当然可以的,于是我们就想着去改顺序表的指向嘛,这时候就会发现,欸,typedef的好处就来了:
//typedef int SQLDataType;
//前
typedef PersonInfo SQLDataType;
//后
我们直接把int改为PersonInfo不就OK了?铁汁们你们说O不OK? 😎
接下来我们把顺序表的.h文件包一下Contact.h文件,再将Contact.c文件包含一下顺序表的.h文件,我们就可以开始实现我们的通讯录的功能了!:
Contact.c:
初始化:
void SeqListInit(SeqList* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
//这里把顺序表的初始化放这里,方便阅读
void LoadContact(Contact* con)
{
FILE* pf = fopen("Contact.txt", "rb");//用二进制的方法打开文件Contact.txt
if (pf == NULL)//若打开失败
{
perror("FILE fopen fail!");
return;
}
PersonInfo info;//这里定义了一个指向通讯录结构体的结构体变量info
while (fread(&info, sizeof(PersonInfo), 1, pf))//若读取通讯录里的内容成功
{
SeqListPushBack(con, info);//顺序表的尾插,(篇幅原因,博主在下面放上代码)
printf("数据存入成功!\n");
}
}
void InitContact(Contact* con)
{
SeqListInit(con);//顺序表的初始化
LoadContact(con);
}
由于篇幅原因,姑且将顺序表的尾插放在这里了(包括内容的检查):
void CheckCapacity(SeqList* ps)
{
if (ps->capacity == ps->size)
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//SQLDataType* tmp = (SQLDataType*)malloc(newCapacity * sizeof(SQLDataType));
SQLDataType* tmp = (SQLDataType*)realloc(ps->arr, newCapacity * sizeof(SQLDataType));
if (tmp == NULL)
{
perror("realloc fail!");
return;
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
void SeqListPushBack(SeqList* ps, SQLDataType x)
{
assert(ps);
CheckCapacity(ps);
ps->arr[ps->size] = x;
ps->size++;
}
博主在这里在多嘴一句关于文件的操作:
fopen用于打开文件,如果文件成功打开,它返回一个指向文件的指针,否则返回NULL。
源代码声明如下:
FILE *fopen(const char *filename, const char *mode);
- mode:文件打开方式
"rb" 通常是用二进制打开并读取文件;
fread用于从文件读取数据,允许从文件中读取指定数量的数据块,并将其存储到指定的内存位置。源代码声明如下:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- ptr: 指向存储数据的内存块的指针。
- size: 每个数据块的字节数。
- nmemb: 要读取的数据块的数量。
- stream: 文件指针,指向要读取的文件
添加联系人:
void AddContact(Contact* con)
{
PersonInfo Info;
printf("请输入姓名:\n");
scanf("%s", &Info.name);
printf("请输入性别:\n");
scanf("%s", &Info.gender);
printf("请输入年龄:\n");
scanf("%d", &Info.age);
printf("请输入电话号码:\n");
scanf("%s", &Info.tel);
printf("请输入住址:\n");
scanf("%s", &Info.addre);
SeqListPushBack(con, Info);//同样是对PersonInfo进行尾插(存联系人数据)
printf("信息存入成功 \n");
}
删除联系人:
由于我们后面修改联系人也需要查找联系人,所以我们就封装一个FindByName函数,以供以后调用。
int FindByName(Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, name))//strcmp函数对比输入的名字与已经存在于通讯录里的名字一一进行比较
return i;//若找到了,返回这个联系人在通讯录里的位置
}
return -1;//若找不到,返回无效位置
}
void DelContact(Contact* con)
{
char name[NAME_MAX];
printf("请输入要删除的用户的名字:\n");
scanf("%s", name);
int pos = FindByName(con, name);//寻找要删除的联系人
if (pos < 0)//若要删除的联系人不存在
{
printf("查找的用户不存在!\n");
return;
}
SeqListErase(con, pos);//调用顺序表删除函数
printf("删除成功!\n");
}
展示所有联系人信息:
void ShowContact(Contact* con)
{
printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "联系电话", "地址");
//格式可以自己调整,好看就行了
for (int i = 0; i < con->size; i++)
{
printf("%-10s %-4s %-4d %15s %-20s\n",
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tel,
con->arr[i].addre);
}
}
寻找联系人:
void FindContact(Contact* con)
{
char name[NAME_MAX];
printf("请输入要查找的用户的名字:\n");
scanf("%s", name);
int pos = FindByName(con, name);
if (pos < 0)
{
printf("查找的用户不存在!\n");
return;
}
printf("查找成功!");
printf("%-10s %-4s %-4d %15s %-20s\n",
con->arr[pos].name,
con->arr[pos].gender,
con->arr[pos].age,
con->arr[pos].tel,
con->arr[pos].addre);
}
//没啥好说的
修改联系人信息:
void ModifyContact(Contact* con)
{
char name[NAME_MAX];
printf("请输入要修改的用户的名字:\n");
scanf("%s", name);
int pos = FindByName(con, name);
if (pos < 0)
{
printf("查找的用户不存在!\n");
return;
}
//寻找联系人操作如上面
printf("请输入姓名:\n");
scanf("%s", &con->arr[pos].name);
printf("请输入性别:\n");
scanf("%s", &con->arr[pos].gender);
printf("请输入年龄:\n");
scanf("%d", &con->arr[pos].age);
printf("请输入电话号码:\n");
scanf("%s", &con->arr[pos].tel);
printf("请输入住址:\n");
scanf("%s", &con->arr[pos].addre);
//修改联系人,即为内容的覆盖
printf("修改成功!");
}
摧毁通讯录:
在摧毁通讯录前,我们还想把之前存储(操作)的内容保存在文件中,所以包装了一个保存数据的函数:
void SaveContact(Contact* con)
{
FILE* pf = fopen("Contact.txt", "wb");//用二进制的形式打开文件以进行写入操作
if (pf == NULL)
{
perror("FILE fopen fail!");
return;
}
for (int i = 0; i < con->size; i++)
{
fwrite(con->arr + i, sizeof(PersonInfo), 1, pf);//每次写入一个联系人的信息
}
printf("通讯录保存成功!");
}
void DestroyContact(Contact* con)
{
SaveContact(con);
SeqListDestory(con);//调用顺序表的摧毁函数
}
● "wb" 用于以二进制模式打开文件以进行写入操作
● fwrite 用于将数据写入文件的函数。源码的声明如下:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
//可以看到和fread一样
OK,到此为止就是通讯录的全部实现代码,下面就是我们的测试test.c文件的玩我们的功能了,当然为了面向用户(高级说法),就是便于阅读和使用,我们可以随便写个使用表(别忘了包含头文件哦~):
test.c:
void menu()
{
Contact con;
InitContact(&con);
int op = -1;
do
{
printf("*******************************\n");
printf("****1.添加用户 2.删除用户****\n");
printf("****3.查找用户 4.修改用户****\n");
printf("****5.展示用户 0.退出 ****\n");
printf("*******************************\n");
printf("请输入你的选择: \n");
scanf("%d", &op);
switch (op)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
FindContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 0:
printf("退出联系表");
break;
default:
printf("选择错误,请重新选择:\n");
break;
}
} while (op != 0);
DestroyContact(&con);//别忘了摧毁,不然会造成内存泄漏
}
int main()
{
menu();
return 0;
}
到这里我们的通讯录(顺序表版)就已经完成了,既然都花费了这么长时间来写,不玩一玩怎么对得起这个时间呢~ ^_~
添加联系人,展示联系人get!
删除联系人,修改联系人get!
最后我们来看一下Contact.txt文件里的内容:
摧毁加保存get!
但是,我们可以发现用记事本打开的文件里面存的是一堆乱码,这是什么原因呢?其实就是我们文件读取的方式问题,我们的记事本是以文本形式打开文件的,他读不懂二进制,所以显示的就是一队乱码,那如果我们想读取二进制文件,我们可以用专门的软件或插件打开(网上有许多教程,博主这里就不演示了)
志不立,天下无可成之事。——王阳明
本篇博客也就到此为止了,送大家一碗鸡汤,勉励自己以及这世界上所有追逐梦想的赤子趁年华尚好努力提升自己,莫欺少年穷!