标题我的第一个小项目——通讯录(黑框的)
附上源码(有错误欢迎大家留言)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
// 链表的创建
typedef struct NODE
{
int id; //联系人编号
char *tel; //联系人电话号码
char *name; //联系人姓名
struct NODE * pNext; //存储下个联系人
}List;
// 分页链表
typedef struct Page {
int TotalPage; //总共的页数
int CurrentPage; //当前在第几页
int TotalInfo; //总共多少条消息
int OnePageInfo; //一页显示多少条消息
}Page;
int g_Menu; //菜单编号,根据不同的编号调用不同的菜单
char g_Key;
/*================================================================================================*/
// 主菜单
void MainMenu();
/*================================================================================================*/
// 1、查看通讯录(分页功能、上一页、下一页,返回主菜单)
//分页的信息(对总条数进行处理,把处理后的数据放到分页的结构体中)
Page* GetPage(List *pHead, int num); //num为一页显示多少条信息
//显示分页的信息(把每一页上的联系人给显示出来)
void ShowPage(List *pHead, Page *pPage);
//按键功能(吸取用户输入的字符,根据字符进行翻页)
char GetKey();
//小菜单(显示上一页、下一页等信息)
void ShowMenu(Page *pPage);
//翻页功能+把每页上的信息给显示出来,所以里面还会调用 ShowPage(List *pHead,Page *pPage)
void TurnPage(List *pHead, Page *pPage);
//查看功能(把链表的头传入,显示全部的信息)
void Browse(List *pHead);
/*================================================================================================*/
//2、 添加通讯录的功能
//自动获得id
int GetId();
//自动获得手机号
char* GetTel();
//自动获得姓名
char* GetName();
//获得联系人->获得节点
List* GetNode();
//将节点(联系人)添加到链表中
void AddNode(List **ppHead, List **ppEnd, List *pTemp);
//初始化数据,添加多少个联系人
void InitInfo(List **ppHead, List **ppEnd, int num);
//获得输入的字符串:姓名、手机号等
char* GetString();
//手动添加一个联系人,自己输入名字、电话号码
List* GetNodeIn();
/*================================================================================================*/
// 3 查询功能(输入关键字支持前缀模糊搜索,查询结果分页显示、附加菜单、重新查询)
void Query(List *pHead);
/*================================================================================================*/
//4 删除功能(查询、增加按钮删除信息、支持继续删除y)
//删除节点
void DeleteNode(List **ppHead, List **ppEnd, int id);
//删除操作
void DeleteInfo(List **ppHead, List **ppEnd);
/*================================================================================================*/
//5 修改功能(查询、增加按钮u修改信息)
void UpdateInfo(List *pHead);
int main() {
srand((unsigned int)time(NULL)); //随机数种子
List *pHead = NULL; // 链表的头
List *pEnd = NULL; //链表的尾
char c;
InitInfo(&pHead, &pEnd, 101); //添加101个联系人
while (1) {
MainMenu();
c = GetKey();
switch (c)
{
case '1': //查看通讯录
g_Menu = 1;
Browse(pHead);
break;
case '2': //添加信息
AddNode(&pHead, &pEnd, GetNodeIn());
break;
case '3':
g_Menu = 3;
Query(pHead);
break;
case '4':
g_Menu = 4;
DeleteInfo(&pHead, &pEnd);
break;
case '5':
g_Menu = 5;
UpdateInfo(pHead);
break;
case 'q':
exit(0);
break;
default:
break;
}
}
return 0;
}
// 主菜单
void MainMenu() {
printf("1.查看通讯录\n");
printf("2.添加信息\n");
printf("3.查询信息\n");
printf("4.删除信息\n");
printf("5.修改信息\n");
printf("q.退出\n");
}
/*================================================================================================*/
// 查看通讯录(分页功能、上一页、下一页,返回主菜单)
//分页的信息(对总条数进行处理,把处理后的数据放到分页的结构体中)num为一页显示多少条信息
Page* GetPage(List *pHead, int num) {
Page *page = (Page *)malloc(sizeof(Page));
page->OnePageInfo = num;
page->CurrentPage = 0;
page->TotalInfo = 0;
page->TotalPage = 0;
while (pHead != NULL)
{
(page->TotalInfo)++;
pHead = pHead->pNext;
}
if (page->TotalInfo % page->OnePageInfo == 0) {
page->TotalPage = page->TotalInfo / page->OnePageInfo;
}
else {
page->TotalPage = page->TotalInfo / page->OnePageInfo + 1;
}
return page;
}
//显示分页的信息(把每一页上的联系人给显示出来)
void ShowPage(List *pHead, Page *pPage) {
int begin = (pPage->CurrentPage - 1) * 10 + 1;
int end = (pPage->CurrentPage) * 10;
int count = 0;
while (pHead != NULL) {
count++;
if (count >= begin && count <= end) {
printf("%d %s %s\n", pHead->id, pHead->name, pHead->tel);
}
pHead = pHead->pNext;
}
}
//按键功能(吸取用户输入的字符,根据字符进行翻页)
char GetKey() {
char c, y;
int flag = 1;
while ((c = getchar()) != '\n' || flag) { //防止在输入之前有个空格 用flag标记一下 确保循环执行一次
y = c; //c = '\n'时就会返回'\n',用y记录下c的值,返回y
flag = 0;
}
return y;
}
//小菜单(显示上一页、下一页等信息)
void ShowMenu(Page *pPage) {
switch (g_Menu)
{
case 1:
printf("当前第%d页 总共%d页 总共%d条 k上一页 m下一页 b返回主菜单\n", pPage->CurrentPage, pPage->TotalPage, pPage->TotalInfo);
break;
case 3:
printf("当前第%d页 总共%d页 总共%d条 k上一页 m下一页 c重新查询 b返回主菜单\n", pPage->CurrentPage, pPage->TotalPage, pPage->TotalInfo);
break;
case 4:
printf("当前第%d页 总共%d页 总共%d条 k上一页 m下一页 d删除信息 b返回主菜单\n", pPage->CurrentPage, pPage->TotalPage, pPage->TotalInfo);
break;
case 5:
printf("当前第%d页 总共%d页 总共%d条 k上一页 m下一页 u修改信息 b返回主菜单\n", pPage->CurrentPage, pPage->TotalPage, pPage->TotalInfo);
break;
default:
break;
}
}
//翻页功能+把每页上的信息给显示出来,所以里面还会调用 ShowPage(List *pHead,Page *pPage)
void TurnPage(List *pHead, Page *pPage) {
//因为初始化时默认当前页为0,查看时要把第一页给显示出来,所以给c赋值‘k’,确保开始显示出来第一页
char c = 'm';
while (1)
{
switch (c)
{
case 'm': //下一页
if (pPage->CurrentPage < pPage->TotalPage) { //判断是否为最后一页
pPage->CurrentPage++;
ShowPage(pHead, pPage); //把信息显示出来
ShowMenu(pPage); //在信息的下面显示出来小菜单
}
else {
printf("已经是最后一页了\n");
}
break;
case 'k': //上一页
if (pPage->CurrentPage > 1) { //判断是否为第一页
pPage->CurrentPage--;
ShowPage(pHead, pPage);
ShowMenu(pPage);
}
else {
printf("当前已经是第一页了\n");
}
break;
case 'b': //返回主菜单
return;
break;
case 'c':
return;
break;
case 'd':
return;
break;
case 'u':
return;
break;
default:
break;
}
c = GetKey(); //输入一个字母进行上一页下一页
g_Key = c;
}
}
//查看功能(把链表的头传入,显示全部的信息)
void Browse(List *pHead) {
Page *pPage = GetPage(pHead, 10);
TurnPage(pHead, pPage);
free(pPage); //用完之后清掉空间
pPage = NULL;
}
/*================================================================================================*/
// 添加通讯录的功能
//自动获得id
int GetId() {
static int id = 1;
return id++;
}
//自动获得手机号
char* GetTel() {
int i;
char *tel = (char *)malloc(12);
for (i = 0; i < 11; i++) {
if (i == 0) {
tel[i] = '1';
continue;
}
if (i == 1) {
if (rand() % 3 == 0) {
tel[i] = '3';
continue;
}
else if (rand() % 3 == 1) {
tel[i] = '5';
continue;
}
else {
tel[i] = '8';
continue;
}
}
tel[i] = rand() % 10 + '0';
}
tel[i] = '\0';
return tel;
}
//自动获得姓名
char* GetName() {
int i;
char *name = (char *)malloc(6);
for (i = 0; i < 5; i++) {
name[i] = rand() % 26 + 'a';
}
name[i] = '\0';
return name;
}
//获得联系人->获得节点
List* GetNode() {
List *pTemp = (List *)malloc(sizeof(List));
pTemp->id = GetId(); //方便测试,应设置自动获取id,并且有序
pTemp->name = GetName(); //自动获得名字
pTemp->tel = GetTel(); //自动获得电话号码
pTemp->pNext = NULL;
return pTemp;
}
//将节点(联系人)添加到链表中
void AddNode(List **ppHead, List **ppEnd, List *pTemp) {
//如果链表中没有节点
if (*ppHead == NULL) {
*ppHead = pTemp;
*ppEnd = pTemp;
return;
}
//如果有节点直接插入到尾
(*ppEnd)->pNext = pTemp;
*ppEnd = pTemp;
return;
}
//初始化数据,添加多少个联系人
void InitInfo(List **ppHead, List **ppEnd, int num) {
int i;
for (i = 0; i < num; i++) {
AddNode(ppHead, ppEnd, GetNode());
}
}
//获得输入的字符串:姓名、手机号等
char* GetString() {
char c;
int count = 0;
int size = 5;
char *str = (char *)malloc(5);
char *newstr = NULL; //备用空间
while ((c = getchar()) != '\n')
{
str[count] = c;
count++;
if (count + 1 == size) {
str[count] = '\0';
size += 5;
newstr = (char*)malloc(size);
strcpy(newstr, str);
free(str);
str = newstr;
}
}
str[count] = '\0';
return str;
}
//手动添加一个联系人,自己输入名字、电话号码
List* GetNodeIn() {
List *pTemp = (List *)malloc(sizeof(List));
pTemp->id = GetId();
printf("请输入您要添加的联系人的姓名:\n");
pTemp->name = GetString();
printf("请输入您要添加的联系人的手机号:\n");
pTemp->tel = GetString();
pTemp->pNext = NULL;
return pTemp;
}
/*================================================================================================*/
// 3 查询功能(输入关键字支持前缀模糊搜索,查询结果分页显示、附加菜单、重新查询)
void Query(List *pHead) {
//把模糊搜索出来的重新置成一个链表
List *newpHead = NULL;
List *newpEnd = NULL;
List *pMark = pHead;
List *pDel = NULL;
char *str = NULL;
char c;
//因为要重新查询,所以有个大循环
while (1)
{
//因为可以不确认,重新输入,这里也要用个循环
while (1)
{
printf("请输入关键字:\n");
str = GetString();
printf("a确认 其他重新输出:\n");
c = GetKey();
if (c == 'a') {
break;
}
else {
free(str);
str = NULL;
}
}
pHead = pMark; //每次遍历前都让头指针重新指回头,因为经过循环后,pHead = NULL
//遍历链表去寻找有关键字的电话或者姓名
while (pHead != NULL)
{
if (strncmp(pHead->name, str, strlen(str)) == 0 || strncmp(pHead->tel, str, strlen(str)) == 0) {
List *pTemp = (List *)malloc(sizeof(List));
pTemp->id = pHead->id;
pTemp->tel = pHead->tel;
pTemp->name = pHead->name;
pTemp->pNext = NULL;
AddNode(&newpHead, &newpEnd, pTemp); //将查询到的联系人放入新的链表中
}
pHead = pHead->pNext;
}
Browse(newpHead); //将查询到的结果分页显示出来、这里也要有个专门的小菜单、附加重新查询的功能
// 查看完后可能又会进行一次查询,我们要把已经查询完的链表给清掉
while (newpHead != NULL) {
pDel = newpHead;
newpHead = newpHead->pNext;
free(pDel);
pDel = NULL;
}
newpEnd = NULL;
if (g_Key == 'b' || g_Key == 'd' || g_Key == 'u') {
break;
}
}
}
/*================================================================================================*/
//4 删除功能(查询、增加按钮删除信息、支持继续删除y)
//删除节点
void DeleteNode(List **ppHead, List **ppEnd, int id) {
List *pDel = NULL;
List *pTemp = *ppHead;
//判断一下是否头删除
if (pTemp->id == id) {
pDel = pTemp;
pTemp = pTemp->pNext;
free(pDel);
pDel = NULL;
return;
}
while (pTemp->pNext != NULL)
{
if (pTemp->pNext->id == id) {
pDel = pTemp->pNext;
pTemp->pNext = pTemp->pNext->pNext;
free(pDel);
pDel = NULL;
if (pTemp->pNext == NULL) {
*ppEnd = pTemp;
}
}
pTemp = pTemp->pNext;
}
return;
}
//删除操作(可以继续删除)
void DeleteInfo(List **ppHead, List **ppEnd) {
char *str = NULL;
int id = 0;
//首先要查询功能
while (1)
{
Query(*ppHead); //这里也要有个小菜单
if (g_Key == 'b') {
break;
}
printf("请输入您要删除的编号:\n");
str = GetString();
id = atoi(str);
free(str);
str = NULL;
DeleteNode(ppHead, ppEnd, id);
printf("按y继续删除 其他键返回主菜单\n");
if (GetKey() != 'y') {
break;
}
}
}
/*================================================================================================*/
//5 修改功能(查询、增加按钮u修改信息)
void UpdateInfo(List *pHead) {
char *str = NULL;
int id = 0;
Query(pHead);
printf("请输入您要修改的编号:\n");
str = GetString();
id = atoi(str);
free(str);
str = NULL;
//遍历链表
while (pHead != NULL)
{
if (pHead->id == id) {
printf("请输入您要修改的姓名:\n");
str = GetString();
if (strlen(str) > 0) {
free(pHead->name);
pHead->name = str;
}
printf("请输入你要修改的手机号:\n");
str = GetString();
if (strlen(str) > 0) {
free(pHead->tel);
pHead->tel = str;
}
}
pHead = pHead->pNext;
}
return;
}