[C语言]C语言实战项目------单链表通讯录
今天我们将用单链表的知识去完成一个通讯录。
我们需要完成通讯录的增删查改以及通讯录每次写入的内容的存储和下次使用的导入。
一、节点的声明
老样子我们先在头文件定义节点和有关的数据的大小
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#define NAME_MAX 15
#define SEX_MAX 10
#define TEL_MAX 15
#define ADDR_MAX 25
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
typedef struct List
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
struct List* next;
}Info;
typedef struct contact
{
Info data; //节点数据
struct contact* next; //指针保存下个节点的地址
}contact;
接着我们先声明相关函数
//初始化通讯录
void LoadContact(contact** head);
//增加联系人
void AddContact(contact** head);
//查看联系人
void ShowContact(const contact** head);
//修改联系人信息
void ModifyContact(contact** head);
//查找联系人
void SearchContact(const contact** head);
//删除联系人
void DelContact(contact** head);
//将通讯录中的联系人保存到文件中
void SaveContact(contact** head);
//销毁通信录
void DestroyContact(contact** head);
//导入数据
void LoadContact(contact** head);
二、初始化通讯录
我们先把从文件中导入数据作为开始
void LoadContact(contact** head)
{
FILE* Pr = fopen("Contact.txt", "r");
if (Pr ==NULL)
{
perror("LoadContact");
exit(0);
}
printf("加载通讯录中......\n"); while (fgetc(Pr) != '\n');//将文件指针移动到表头末尾
int leap = ftell(Pr);//计算文件指针的偏移量
fseek(Pr, leap, SEEK_SET);//将文件指针后移,跳过表头,只读取数据
while (!feof(Pr))
{
contact* pnew = *head;
contact* pmove = *head;
pnew = (contact*)malloc(sizeof(contact));//开辟空间
pnew->next = NULL;
//将文件内容写入节点中
fscanf(Pr, "%s\t%s\t%d\t%s\t%s\n", pnew->data.name,
pnew->data.sex,
&(pnew->data.age),
pnew->data.tel,
pnew->data.addr);
//链接节点
if (*head == NULL)//第一次Load
{
*head = pnew;
pnew->next = NULL;
}
else
{
while (pmove->next != NULL)
{
pmove = pmove->next;
}
pmove->next = pnew;
pnew->next = NULL;
}
}
printf("加载成功!\n");
}
三、增加联系人
我们采用尾插的办法:
void AddContact(contact** head)
{
assert(head);
contact* newnode = *head;
contact* pmove = *head;
newnode = (contact*)malloc(sizeof(contact));
printf("请输入姓名:>");
scanf("%s", newnode->data.name);
printf("请输入性别:>");
scanf("%s", newnode->data.sex);
printf("请输入年龄:>");
scanf("%d", &(newnode->data.age));
printf("请输入电话:>");
scanf("%s", newnode->data.tel);
printf("请输入地址:>");
scanf("%s", newnode->data.addr);
newnode->next = NULL;
if (*head == NULL)//第一次添加
{
*head = newnode;
newnode->next = NULL;
}
else
{
while (pmove->next != NULL)//以后添加节点
{
pmove = pmove->next;//找到尾节点
}
pmove->next = newnode;
newnode->next = NULL;
}
}
四、查看联系人
遍历链表然后打印
void ShowContact(const contact** head)
{
system("cls");
contact* pmove = *head;
printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "地址");//打印一个表头在屏幕上
while (pmove != NULL)//循环到最后一个节点停止
{
printf("%s\t%s\t%d\t%s\t%s\n", pmove->data.name,
pmove->data.sex,
pmove->data.age,
pmove->data.tel ,
pmove->data.addr);
pmove = pmove->next;
}
}
五、修改联系人
遍历链表比较名字,然后修改相应节点的数据
void ModifyContact(contact** head)
{
char name[20];
printf("请输入你要修改联系人的姓名\n");
scanf("%s", name);
contact* pos = *head;
//找到该联系人
while (strcmp(pos->data.name, name) != 0 && pos->next != NULL)
{
pos = pos->next;
}
if (strcmp(pos->data.name, name) == 0)
{
printf("请输入姓名:");
scanf("%s", pos->data.name);
printf("请输入性别:");
scanf("%s", pos->data.sex);
printf("请输入年龄:");
scanf("%d", &(pos->data.age));
printf("请输入电话:");
scanf("%s", pos->data.tel);
printf("请输入地址:");
scanf("%s", pos->data.addr);
printf("修改成功!\n");
}
else
{
printf("未查找到该联系人,无法修改\n");
exit(0);
}
}
六、查找联系人
遍历链表比较名字
void SearchContact(const contact** head)
{
char name[20];
printf("请输入你要查找联系人的姓名:>");
scanf("%s", name);
contact* pos = *head;
//找到要查看到节点
while (strcmp(pos->data.name, name) != 0 && pos->next != NULL)
{
pos = pos->next;
}
if (strcmp(pos->data.name, name) == 0)
{
printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "地址");//打印一个表头,要注意打印形式
printf("%s\t%s\t%d\t%s\t%s\n", pos->data.name,
pos->data.sex,
pos->data.age,
pos->data.tel,
pos->data.addr);
}
else
printf("无此联系人!\n");
}
七、删除联系人
遍历链表比较名字,然后删除节点
void DelContact(contact** head)
{
assert(head);
if (*head == NULL)
{
printf("通讯录为空,删除失败!\n");
exit(0);
}
char name[20];
printf("请输入要删除联系人的姓名:>");
scanf("%s", name);
//删除
contact* pmove = *head;
contact* plast = *head;
//找到要删除的节点
while (strcmp(pmove->data.name, name) != 0 && pmove->next != NULL)
{
plast = pmove;//保存移动节点的上一个节点
pmove = pmove->next;
}
if (strcmp(pmove->data.name, name) == 0)
{
//删除的是表头节点
if (pmove == *head)
*head = pmove->next;
else//删除的是中间的节点
plast->next = pmove->next;
printf("删除成功!\n");
}
else
printf("无此联系人,无法删除!\n");
}
八、将通讯录中的联系人保存到文件中
写入整个链表的数据
void SaveContact(contact** head)
{
assert(*head);
contact* pmove = *head;
FILE* pr = fopen("Contact.txt", "w");
if (pr == NULL)
{
perror("SaveContact");
exit(0);
}
int i = 0;
fprintf(pr, "%s\t%s\t%s\t%s\t%s\n",
"姓名", "性别", "年龄", "电话", "地址");
while (pmove != NULL)
{
fprintf(pr, "%s\t%s\t%d\t%s\t%s\n", pmove->data.name,
pmove->data.sex,
pmove->data.age,
pmove->data.tel,
pmove->data.addr);
pmove = pmove->next;
}
fclose(pr);
pr = NULL;
}
九、销毁通信录
动态开辟的空间需要销毁,思路和我们之前的单链表一样
void DestroyContact(contact** head)
{
contact* pmove = *head;
while (*head != NULL)
{
pmove = *head;//记录表头的地址
*head = (*head)->next;//表头后移,直到最后一个节点
free(pmove);
pmove->next = NULL;
}
}
十、整体代码展示
//Contact.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#define NAME_MAX 15
#define SEX_MAX 10
#define TEL_MAX 15
#define ADDR_MAX 25
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
typedef struct List
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
struct List* next;
}Info;
typedef struct contact
{
Info data; //节点数据
struct contact* next; //指针保存下个节点的地址
}contact;
//初始化通讯录
void LoadContact(contact** head);
//增加联系人
void AddContact(contact** head);
//查看联系人
void ShowContact(const contact** head);
//修改联系人信息
void ModifyContact(contact** head);
//查找联系人
void SearchContact(const contact** head);
//删除联系人
void DelContact(contact** head);
//将通讯录中的联系人保存到文件中
void SaveContact(contact** head);
//销毁通信录
void DestroyContact(contact** head);
//Contact.c
#include "Contact.h"
void LoadContact(contact** head)
{
FILE* Pr = fopen("Contact.txt", "r");
if (Pr ==NULL)
{
perror("LoadContact");
exit(0);
}
printf("加载通讯录中......\n"); while (fgetc(Pr) != '\n');//将文件指针移动到表头末尾
int leap = ftell(Pr);//计算文件指针的偏移量
fseek(Pr, leap, SEEK_SET);//将文件指针后移,跳过表头,只读取数据
while (!feof(Pr))
{
contact* pnew = *head;
contact* pmove = *head;
pnew = (contact*)malloc(sizeof(contact));//开辟空间
pnew->next = NULL;
//将文件内容写入节点中
fscanf(Pr, "%s\t%s\t%d\t%s\t%s\n", pnew->data.name,
pnew->data.sex,
&(pnew->data.age),
pnew->data.tel,
pnew->data.addr);
//链接节点
if (*head == NULL)//第一次Load
{
*head = pnew;
pnew->next = NULL;
}
else
{
while (pmove->next != NULL)
{
pmove = pmove->next;
}
pmove->next = pnew;
pnew->next = NULL;
}
}
printf("加载成功!\n");
}
void AddContact(contact** head)
{
assert(head);
contact* newnode = *head;
contact* pmove = *head;
newnode = (contact*)malloc(sizeof(contact));
printf("请输入姓名:>");
scanf("%s", newnode->data.name);
printf("请输入性别:>");
scanf("%s", newnode->data.sex);
printf("请输入年龄:>");
scanf("%d", &(newnode->data.age));
printf("请输入电话:>");
scanf("%s", newnode->data.tel);
printf("请输入地址:>");
scanf("%s", newnode->data.addr);
newnode->next = NULL;
if (*head == NULL)//第一次添加
{
*head = newnode;
newnode->next = NULL;
}
else
{
while (pmove->next != NULL)//以后添加节点
{
pmove = pmove->next;//找到尾节点
}
pmove->next = newnode;
newnode->next = NULL;
}
}
void ShowContact(const contact** head)
{
system("cls");
contact* pmove = *head;
printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "地址");//打印一个表头在屏幕上
while (pmove != NULL)//循环到最后一个节点停止
{
printf("%s\t%s\t%d\t%s\t%s\n", pmove->data.name,
pmove->data.sex,
pmove->data.age,
pmove->data.tel ,
pmove->data.addr);
pmove = pmove->next;
}
}
void DelContact(contact** head)
{
assert(head);
if (*head == NULL)
{
printf("通讯录为空,删除失败!\n");
exit(0);
}
char name[20];
printf("请输入要删除联系人的姓名:>");
scanf("%s", name);
//删除
contact* pmove = *head;
contact* plast = *head;
//找到要删除的节点
while (strcmp(pmove->data.name, name) != 0 && pmove->next != NULL)
{
plast = pmove;//保存移动节点的上一个节点
pmove = pmove->next;
}
if (strcmp(pmove->data.name, name) == 0)
{
//删除的是表头节点
if (pmove == *head)
*head = pmove->next;
else//删除的是中间的节点
plast->next = pmove->next;
printf("删除成功!\n");
}
else
printf("无此联系人,无法删除!\n");
}
void SearchContact(const contact** head)
{
char name[20];
printf("请输入你要查找联系人的姓名:>");
scanf("%s", name);
contact* pos = *head;
//找到要查看到节点
while (strcmp(pos->data.name, name) != 0 && pos->next != NULL)
{
pos = pos->next;
}
if (strcmp(pos->data.name, name) == 0)
{
printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "地址");//打印一个表头,要注意打印形式
printf("%s\t%s\t%d\t%s\t%s\n", pos->data.name,
pos->data.sex,
pos->data.age,
pos->data.tel,
pos->data.addr);
}
else
printf("无此联系人!\n");
}
void ModifyContact(contact** head)
{
char name[20];
printf("请输入你要修改联系人的姓名\n");
scanf("%s", name);
contact* pos = *head;
//找到该联系人
while (strcmp(pos->data.name, name) != 0 && pos->next != NULL)
{
pos = pos->next;
}
if (strcmp(pos->data.name, name) == 0)
{
printf("请输入姓名:");
scanf("%s", pos->data.name);
printf("请输入性别:");
scanf("%s", pos->data.sex);
printf("请输入年龄:");
scanf("%d", &(pos->data.age));
printf("请输入电话:");
scanf("%s", pos->data.tel);
printf("请输入地址:");
scanf("%s", pos->data.addr);
printf("修改成功!\n");
}
else
{
printf("未查找到该联系人,无法修改\n");
exit(0);
}
}
void SaveContact(contact** head)
{
assert(*head);
contact* pmove = *head;
FILE* pr = fopen("Contact.txt", "w");
if (pr == NULL)
{
perror("SaveContact");
exit(0);
}
int i = 0;
fprintf(pr, "%s\t%s\t%s\t%s\t%s\n",
"姓名", "性别", "年龄", "电话", "地址");
while (pmove != NULL)
{
fprintf(pr, "%s\t%s\t%d\t%s\t%s\n", pmove->data.name,
pmove->data.sex,
pmove->data.age,
pmove->data.tel,
pmove->data.addr);
pmove = pmove->next;
}
fclose(pr);
pr = NULL;
}
void DestroyContact(contact** head)
{
contact* pmove = *head;
while (*head != NULL)
{
pmove = *head;//记录表头的地址
*head = (*head)->next;//表头后移,直到最后一个节点
free(pmove);
pmove->next = NULL;
}
}
//test.c
#include"Contact.h"
void MakeMenu()
{
printf("***************************************\n");
printf("******1. 新建联系人 2. 删除联系人*******\n");
printf("******3.查找联系人 4. 显示联系人*******\n");
printf("******5. 修改信息 0. 退出通讯录*******\n");
printf("***************************************\n");
};
int main()
{
contact* head = NULL;
LoadContact(&head);
int input = 0;
do
{
MakeMenu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&head);//增加
break;
case 2:
DelContact(&head);//删除
break;
case 3:
SearchContact(&head);//查找
break;
case 4:
ShowContact(&head);//显示所有联系人
break;
case 5:
ModifyContact(&head);//修改信息
break;
case 0:
SaveContact(&head);//保存通讯录到文件中
DestroyContact(&head);//销毁通讯录
printf("退出通讯录!\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}