在上一篇文章我們學會了用單鏈表來實現各種方法,在這一篇文章我們將在單鏈表的基礎上實現通訊錄。
0、準備工作
實現通訊錄之前,我們還需要在單鏈表的基礎上添加2個文件,頭文件Contact.h和源文件Contact.c。Contact.c來實現通訊錄方法的聲明,而Contact.h來實現通訊錄的具體方法。
通訊錄的數據其實就是存儲在單鏈表上的,只不過變成了一個結構體,因此我們需要在Contact.h定義一個聯系人數據的結構體。
如下圖所示:
代碼如下:
typedef struct PersonInfo
{char name[NAME_MAX];char gender[GENDER_MAX];int age;char tel[TEL_MAX];char addr[ADDR_MAX];
}PeoInfo;
為了代碼有更好的延展性,我們還可以對數組的內存大小進行宏定義,在Contact.h的頭部定義:
#define NAME_MAX 10
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100
由于我們是再單鏈表的基礎上進行的,所以我們可以對單鏈表重新起個名字叫做通訊錄,在Contact.h中定義:typedef struct SListNode contact;
由于我們是將類型由整型類型改為結構體類型,那么我們還需要再SList,h中將類型重定義一下:typedef struct PersonInfo SLDataType;
接著就可以在Contact.h中聲明一系列的方法:
//通訊錄的初始化
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);//讀取文件內容到通訊錄
void LoadContact(contact** con);//保存通訊錄數據到文件
void SaveContact(contact** con);
接下來再在Contact.c進行方法的實現,在實現之前我們需要包含以下頭文件:
#include"Contact.h"
#include"SList.h"
1、初始化通訊錄
初始化代碼:
void InitContact(contact** con)
{LoadContact(con);
}
通訊錄的初始化實際上就是將文件的數據導入到通訊錄,確保通訊錄一開始就擁有之前的數據,而不是一個空殼。
將文件數據導入到通訊錄代碼如下:
void LoadContact(contact** con)
{//打開文件FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){perror("fopen fail!");return;}//定義通訊錄變量PeoInfo info;//依次讀取文件數據while (fread(&info, sizeof(info), 1, pf)){//將文件數據尾插到通訊錄中SLTPushBack(con, info);}printf("歷史通訊錄數據導入成功!\n");//有開有閉fclose(pf);
}
2、添加通訊錄數據
思路:創建通訊錄局部變量,輸入對應信息后,再尾插到通訊錄中。
void AddContact(contact** con)
{PeoInfo info;printf("請輸入要添加的聯系人姓名:");scanf("%s", info.name);printf("請輸入要添加的聯系人性別:");scanf("%s", info.gender);printf("請輸入要添加的聯系人年齡:");scanf("%d", &info.age);printf("請輸入要添加的聯系人電話:");scanf("%s", info.tel);printf("請輸入要添加的聯系人地址:");scanf("%s", info.addr);SLTPushBack(con, info);printf("添加聯系人成功!\n");
}
再進行測試:
int main()
{contact* con=NULL;AddContact(&con);return 0;
}
運行結果:
我們再進行調試觀察是否真正的插入成功:
可以觀察到確實成功插入數據了!
3、展示通訊錄數據
為了方便我們觀察方法是否成功進行,我們可以先編寫展示通訊錄數據的方法。
思路:先打印表頭,再創建指針cur指向頭結點,遍歷整個通訊錄,依次打印對應的信息。
void ShowContact(contact* con)
{printf("%-10s %-4s %-4s %-11s %-20s\n", "姓名", "性別", "年齡", "電話", "地址");contact* cur = con;while (cur){printf("%-10s %-4s %4s %-11s %-20s\n", cur->data.name,cur->data.gender,cur->data.age,cur->data.tel,cur->data.addr);cur = cur->next;}
}
再進行測試:
int main()
{contact* con=NULL;AddContact(&con);ShowContact(con);return 0;
}

通訊錄數據顯示成功!
4、刪除通訊錄數據
在進行刪除,修改等一系列操作的時候,我們首先需要知道刪除或修改的對象是誰,因此還需要一個函數專門根據姓名來查找到聯系人,再進行后續的一系列操作。
查找思路:傳一個頭結點,和要查找的姓名,先創建指針指向頭結點,再遍歷通訊錄,看要查找的名字是否在通訊錄中,在就返回下標,不在就返回空。
contact* FindByName(contact* con, char name[])
{contact* cur = con;while (cur){if (0 == strcmp(name, cur->data.name)){return cur;}cur = cur->next;}//沒找到返回NULLreturn NULL;
}
接著就可以刪除通訊錄數據了。
思路:輸入要查找的姓名,調用查找函數找到下標,如果下標為空就表示沒有找到,并且終止程序,下標不為空就執行刪除指定位置數據的操作。
void DelContact(contact** con)
{char name[NAME_MAX];printf("請輸入要刪除的聯系人姓名:");scanf("%s", name);contact* pos = FindByName(*con, name);if (pos == NULL){printf("你要刪除的聯系人不存在!\n");return;}SLTErase(con, pos);printf("刪除成功!\n");
}
再進行測試:
int main()
{contact* con=NULL;AddContact(&con);AddContact(&con);ShowContact(con);DelContact(&con);ShowContact(con);return 0;
}
運行結果:
我們可以觀察到刪除成功!
5、查找通訊錄數據
思路:輸入要查找的姓名,調用查找函數找到下標,下標為空就沒找到并終止程序,不為空就直接展示下標所對應的數據。
void FindContact(contact** con)
{char name[NAME_MAX];printf("請輸入要查找的聯系人姓名:");scanf("%s", name);contact* pos = FindByName(*con, name);if (pos == NULL){printf("你要查找的聯系人不存在!\n");return;}printf("查找成功!\n");//打印下標對應的數據printf("%-10s %-4s %-4s %-11s %-20s\n", "姓名", "性別", "年齡", "電話", "地址");printf("%-10s %-4s %-4d %-11s %-20s\n",pos->data.name,pos->data.gender,pos->data.age,pos->data.tel,pos->data.addr);
}
再進行測試:
int main()
{contact* con=NULL;AddContact(&con);AddContact(&con);FindContact(&con);return 0;
}
運行結果:
6、修改通訊錄數據
思路:輸入要查找的姓名,調用查找函數找到下標,下標為空就沒找到并終止程序,不為空就直接修改下標所對應的數據。
void ModifyContact(contact** con)
{char name[NAME_MAX];printf("請輸入要修改的聯系人姓名:");scanf("%s", name);contact* pos = FindByName(*con, name);if (pos == NULL){printf("你要修改的聯系人不存在!\n");return;}//修改下標對應的數據printf("請輸入要修改的姓名:");scanf("%s", pos->data.name);printf("請輸入要修改的性別:");scanf("%s", pos->data.gender);printf("請輸入要修改的年齡:");scanf("%d", &pos->data.age);printf("請輸入要修改的電話:");scanf("%s", pos->data.tel);printf("請輸入要修改的地址:");scanf("%s", pos->data.addr);printf("修改成功!\n");
}
再進行測試:
int main()
{contact* con=NULL;AddContact(&con);ShowContact(con);ModifyContact(&con);ShowContact(con);return 0;
}
運行結果:
修改成功!
7、保存通訊錄數據
在我們實現以上方法后,我們輸入數據之后可以將其保存,下一次再直接導入使用,因此還需要寫一個保存數據的函數。
思路:打開文件,創建指針cur指向頭結點,遍歷單鏈表,依次將cur對應的聯系人數據寫入文件中。
void SaveContact(contact* con)
{FILE* pf = fopen("contact.txt", "wb");if (pf == NULL){perror("fopen fail!");return;}contact* cur = con;while (cur){//將當前節點對應的聯系人數據寫入文件fwrite(&(cur->data), sizeof(PeoInfo), 1, pf);cur = cur->next;}printf("保存成功!\n");fclose(pf);
}
我們再進行測試:
int main()
{contact* con=NULL;AddContact(&con);SaveContact(con);return 0;
}

可以觀察到,數據已經成功被保存到文件中了!
8、銷毀通訊錄
思路:先保存通訊錄數據,再調用單鏈表的銷毀函數。
void DestroyContact(contact** con)
{SaveContact(*con);SListDestroy(con);
}
再進行測試:
int main()
{contact* con = NULL;AddContact(&con);DestroyContact(&con);return 0;
}
可以觀察到數據已經成功銷毀了,同時數據已經被存儲到文件中了。
9、通訊錄的完整實現
再實現了上述方法之后,我們就可以在test.c中編寫可視化的操作頁面,通過調用通訊錄的方法來完整實現通訊錄了。
思路:先編寫主界面函數,主函數:輸入值先賦值為-1,通訊錄初始化,再使用do while循環條件輸入0終止,調用主界面,輸入值,根據輸入的值使用switch來執行對應的操作,最后再將通訊錄銷毀。
void menu()
{printf("****************** 通訊錄 ******************\n");printf("********* 1、增加聯系人 2、刪除聯系人 **************\n");printf("********* 3、查找聯系人 4、修改聯系人 **************\n");printf("********* 5、展示聯系人 6、保存聯系人 **************\n");printf("********* 0、退出通訊錄 **************\n");
}int main()
{int input = -1;contact* con = NULL;InitContact(&con);do{menu();printf("請選擇你的操作:");scanf("%d", &input);switch (input){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 6:SaveContact(con);break;case 0:printf("退出通訊錄...\n");break;default:printf("輸入錯誤,請重新選擇你的操作:\n");break;}} while (input!=0);DestroyContact(&con);return 0;
}
再進行測試:
發現已經可以實現完整的通訊錄功能了。
點擊在gitee查看完整源代碼