> 文章列表 > C语言小项目 -- 通讯录完整代码(登陆系统+动态开辟 + 文件操作)

C语言小项目 -- 通讯录完整代码(登陆系统+动态开辟 + 文件操作)

C语言小项目 -- 通讯录完整代码(登陆系统+动态开辟 + 文件操作)

目录

📰0. 项目介绍

📰1. 开发环境及框架

📰2. 通讯录账户模块功能分析实现:

📱2.1 通讯录账户菜单界面及数据结构设计

📱2.2 通讯录账户注册功能实现

📱2.3 通讯录账户登录功能实现

📰3. 通讯录模块功能分析实现:

📱3.1 通讯录菜单实现及数据结构设计

📱3.2 通讯录文件加载的实现

📱3.3 通讯录初始化和动态增长的实现

📱3.4 通讯录添加功能的实现

📱3.5 通讯录删除功能的实现

📱3.6 通讯录查找功能的实现

📱3.7 通讯录排序功能的实现

📱3.8 通讯录修改功能的实现

📱3.9 通讯录打印功能的实现

📱3.10 通讯录文件保存及释放空间

📰4. 通讯录完整源代码:

📱4.1 Contacts.h

📱4.2 Contacts.c

📱4.3 ContactsUsers.c

📱4.4 Contacts_Test.c


📰0. 项目介绍

使用C语言设计的一个通讯录管理系统,首先需要账户注册或者账户登录,进入通讯录管理界面,来实现联系人信息的长时间存储(包含姓名、性别、年龄、电话号码等),并且在需要时能够查找出相关联系人的信息。需要包含录入、查找、修改、删除、排序等功能。对通讯录信息存储进行动态内存管理,并以文件格式存取数据。

📰1. 开发环境及框架

本人开发环境采用—VS2022,学会调试,参考文章调试规则,以及windows自带画图板

采取工程内多文件进行链接编译,包括:

  1.  Contacts.h——头文件,所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件。
  2. Contacts.c——源文件,里边含通讯录各种操作函数的具体功能实现
  3. ContactsUsers.c——源文件,包含账户登录注册功能的具体实现
  4. Contacts_Test.c——测试通讯录功能的使用,包含主体运行,主函数等

📰2. 通讯录账户模块功能分析实现:

具体实现功能:

  1. 用户菜单界面的实现
  2. 用户数据结构设计
  3. 用户注册功能的实现(通过文件操作将数据写入文件)
  4. 用户登录功能的实现(通过文件操作将数据从文件读出)

📱2.1 通讯录账户菜单界面及数据结构设计

通过在头文件引用枚举类型,定义枚举常量Option1,实现操作项,增加代码的可维护性;定义结构体类型Users,实现对账号、密码的操作。

账号、密码均采用字符串存储,大小定义为标识符常量,便于进行修改。

void menu1()
{printf("------------------------------\\n");printf("---------- CONTACT -----------\\n");printf("  1.Logo In  *\\n");printf("\\n");printf("  2.Sign In  *\\n");printf("\\n");printf("*(0.exit)*\\n");printf("------------------------------\\n");
}
//打印格式
#define RED "\\033[0;32;31m"
#define GREEN "\\033[0;32;32m"
#define BLUE  "\\033[0;32;34m"
#define NONE "\\033[m"#define	NAME_MAX 20
#define PASSWD_MAX 12
enum Option1
{EXIT1,LOGOIN,SIGNIN,
};
typedef struct Users
{char UserName[NAME_MAX];char UserPasswd[PASSWD_MAX];
}Users;

📱2.2 通讯录账户注册功能实现

文件读取:首先通过只读的方式打开Users.bat二进制文本文件,如果返回NULL,说明文本文件未被创建,便用写的方式进行创建,可以参考文章文件操作

判断账户:进入注册界面,通过两个结构体变量,一个读取用户注册账户名,一个读取文件账户,循环读取文件,并于用户注册账户名进行比较:

        1.使用strcmp,如果输入账户名已经被注册,便return

        2.使用feof( ),如果读取至文件尾,便进行下一步操作

判断密码:通过中间变量进行两次密码确定,且密码位数不得小于五位,如果成功,便将第一个结构体变量通过fwrite写入文件

void UserSign()
{Users user1 = { 0,0 },user2 = {0,0};FILE* pf,*PF = NULL;pf = fopen("Users.bat", "rb");if (pf == NULL){PF = fopen("Users.bat", "wb");fclose(PF);;}pf = fopen("Users.bat", "rb");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf("      Registration interface\\n");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf(BLUE"Please set a new user name:>"NONE);if (scanf("%s", user1.UserName) == EOF){perror("Incorrect input");}printf(GREEN"       Enter successfully\\n"NONE);fread(&user2, sizeof(Users), 1, pf);//【判断】有没有注册过-比较字符串是否相等 
//不相等->是否到文件尾 while (1){if (strcmp(user1.UserName, user2.UserName) != 0){if (feof(pf) == 0)//未到文件尾 fread(&user2, sizeof(Users), 1, pf);else//到了文件尾仍然没有相同的字符串-说明输入的账号使新的 可以去注册界面 {printf(GREEN"The account has not been registered. Rejump\\n"NONE);break;//利用break来跳出无限循环 }}else{printf(RED"err: The account has been registered!\\n"NONE);fclose(pf); pf = NULL;system("pause");return;}}//判断两次密码是否相同,且必须大于5位printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf("      Registration interface       \\n");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf(BLUE"Please set the user password :(English and digital only):>"NONE);if (scanf("%s", user1.UserPasswd) == EOF){perror("Incorrect input");}while (strlen(user1.UserPasswd) < 5){printf(RED"err:The password must be at least six characters!"NONEBLUE"\\nPlease enter the password again:>"NONE);if (scanf("%s", user1.UserPasswd) == EOF){perror("Incorrect input");}}printf(BLUE"Please enter your password again:>"NONE);char tmp[PASSWD_MAX] = { 0 };do {if (EOF == scanf("%s", tmp)){perror("Incorrect input");}if (strcmp(tmp, user1.UserPasswd) != 0)printf(RED"\\nerr:The two passwords are different.\\\\nPlease enter the password again->"NONE);else break;} while (1);//两次密码一致fclose(pf);	pf = NULL;pf = fopen("Users.bat", "ab");if (pf == NULL){perror("UserSign failed");}//fwrite会在当前文件指针的位置写入数据//"w" 打开,文件指针指到头,只写;"a" 打开,指向文件尾else{fwrite(&user1, sizeof(Users), 1, pf);printf(GREEN"     Registered successfully!\\n"NONE);system("pause");fclose(pf);pf = NULL;}return;
}

📱2.3 通讯录账户登录功能实现

返回类型bool,更方便判断是否登陆成功:

                        1.登陆成功,进入通信录内部系统

                        2.登陆失败,重新登录

文件读取:以只读方式打开文件,同上创建两个结构体变量,如果文件为NULL,说明没有用户注册信息,提示需先注册,返回false。

判断账户:进入登陆界面,输入账户,与文件读取账户进行判断,如没有,提示账号未被注册,返回false;如果找到账户,便进行密码判断。

判断密码:使用户具有三次输入机会,如三次机会均输入错误,便返回false。如果正确,便返回true。

bool UserLogo()
{FILE* pf = fopen("Users.bat", "rb");Users user1 = { 0,0 }, user2 = { 0,0 };if (pf == NULL){printf(RED"err:The user directory file does not exist!\\n"NONE"  Please register the user first\\n"NONE);system("pause");return false;}printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf("          Login interface\\n");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf(BLUE"Please enter the account number:>"NONE);if (scanf("%s", user1.UserName) == EOF){perror("Incorrect input");}fread(&user2, sizeof(Users), 1, pf);while (1){if (strcmp(user1.UserName, user2.UserName) != 0){if (feof(pf) == 0)//未到文件尾 fread(&user2, sizeof(Users), 1, pf);else//到了文件尾仍然没有相同的字符串-说明输入的账号新的 可以去注册界面 {printf(GREEN"The account has not been registered. Rejump\\n"NONE);system("pause");fclose(pf);pf = NULL;return false;//利用break来跳出无限循环 }}else{break;}}printf(BLUE"Please enter password:>"NONE);int n = 3;//三次输入机会while (n){if (scanf("%s", user1.UserPasswd) == EOF){perror("Incorrect input");}if (strcmp(user1.UserPasswd, user2.UserPasswd) != 0){if (n == 1){printf(RED"	 err:Login failure\\n"NONE);fclose(pf);pf = NULL;system("pause");return false;}printf(RED"err:Password error\\n"NONEBLUE"Please enter the password again:>"NONE);}else{break;}n--;}printf(GREEN"    Login success!\\n"NONE);getchar();system("pause");fclose(pf); pf = NULL;return true;
}

📰3. 通讯录模块功能分析实现:

具体实现功能:

  1. 通讯录菜单界面的实现
  2. 通讯录数据结构设计
  3. 通讯录文件加载的实现(通过文件操作将数据从文件读出)
  4. 通讯录的初始化,以及动态增长
  5. 通讯录添加功能的实现(通过文件操作将数据写入文件)
  6. 通讯录删除功能的实现(通过文件操作将数据从文件读出)
  7. 通讯录查找功能的实现(通过文件操作将数据读出,用名字或者手机号进行查找)
  8. 通讯录修改功能的实现(通过文件操作将数据写入文件)
  9. 通讯录排序功能的实现
  10. 通讯录的打印(通过文件操作将数据从文件读出)
  11. 保存通讯录到文件并释放空间(通过文件操作将数据写入文件)

📱3.1 通讯录菜单实现及数据结构设计

通过在头文件引用枚举类型,定义枚举常量Option2,实现操作项,增加代码的可维护性;定义结构体类型PeoInfo描述数据信息,通过结构体Contacts实现线性数据结构

对通讯录的操作进而转换为对顺序表的操作

void menu2()
{printf("------------------------------\\n");printf("---------- CONTACT -----------\\n");printf("* 1.add       2.del  *\\n");printf("\\n");printf("* 3.search  4.modify *\\n");printf("\\n");printf("* 5.sort    6.print  *\\n");printf("(0.exit)\\n");printf("------------------------------\\n");
}
#define	NAME_MAX 20
#define	SEX_MAX 5
#define TELE_MAX 12
#define	ADDR_MAX 30#define DEFAULT_SZ 4//初始化容量大小
enum Option2
{EXIT2,ADD,DEL,SEARCH,MODIFY,SORT,PRINT
};
typedef struct PeoInfo
{char name[NAME_MAX];char sex[SEX_MAX];char age;char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;
typedef struct Contacts
{PeoInfo* data;int sz;int capacity;
}Contacts;

📱3.2 通讯录文件加载的实现

通过文件操作函数,将文件数据读出,加载文件由初始化函数自动调用,因此可以设置为内部链接函数。

static void LoadContact(Contacts* con)
{FILE* pf = fopen("contacts.txt", "rb");if (pf == NULL){perror("LoadContact::fopen");return;}PeoInfo tmp = { 0 };while (fread(&tmp, sizeof(PeoInfo), 1, pf)){check_capacity(con);con->data[con->sz] = tmp;con->sz++;}fclose(pf);pf = NULL;
}

📱3.3 通讯录初始化和动态增长的实现

动态开辟空间的优点:

  • 1. 可以动态的申请空间,以便动态确定对象所需要的内存;
  • 2. 便于储存大型对象,通常情况下栈区的大小容不下过于庞大的对象;
  • 3. 传递指针比传递整个对象更方便高效;(当然对于一个变量也可以取地址,但是临时变量的地址是不能长久使用的,特别是返回临时变量的地址是大忌)
  • 4. 同时传递指针可以便于大家共同维护一个空间,指针在传递的过程中,大家都可以修改指针指向的内容,当然要加锁进行保护,这样一处修改,处处修改,否则就要设置全局的变量,大家统一修改使用

参考我的另一篇文章动态内存管理——static使其具有内部链接属性,只能在此文件内部使用

static void check_capacity(Contacts* con)
{if (con->sz == con->capacity){PeoInfo* tmp = (PeoInfo*)realloc(con->data, con->capacity == 0 ? DEFAULT_SZ * sizeof(PeoInfo) : (con->capacity + 2) * sizeof(PeoInfo));if (tmp != NULL){con->data = tmp;}else{perror("check_capacity::realloc");return;}if (con->capacity == 0){con->capacity = 4;}else{con->capacity += 2;}}return;
}
void InitContact(Contacts* con)
{assert(con);con->sz = con->capacity = 0;con->data = NULL;LoadContact(con);
}

📱3.4 通讯录添加功能的实现

通过检查空间大小,直接进行输入

void AddContact(Contacts* con)
{check_capacity(con);system("cls");printf("Add contact information\\n");printf(BLUE"Please enter your name:>"NONE);scanf("%s", con->data[con->sz].name);printf(BLUE"Please enter age:>"NONE);scanf("%d", &(con->data[con->sz].age));printf(BLUE"Please enter gender:>"NONE);scanf("%s", con->data[con->sz].sex);printf(BLUE"Please enter telephone number:>"NONE);scanf("%s", con->data[con->sz].tele);printf(BLUE"Please enter the address:>"NONE);scanf("%s", con->data[con->sz].addr);con->sz++;printf(GREEN"  Add successfully\\n"NONE);system("pause");
}

📱3.5 通讯录删除功能的实现

打印删除菜单——通过名字或者电话号进行删除

可以实现为回调函数,及函数指针传参,通过参数相同,返回值相同,意义相同,便于添加删除方式

static int FindByName(Contacts* con, const char* name)
{assert(con && name);for (int i = 0; i < con->sz; ++i){if (0 == strcmp(con->data[i].name, name)){return i;}}return -1;
}
static int FindByTele(Contacts* con, const char* tele)
{assert(con && tele);for (int i = 0; i < con->sz; ++i){if (0 == strcmp(con->data[i].tele, tele)){return i;}}return -1;
}
static void deletmenu()
{printf("------------------------------\\n");printf("* 1. Delete by name  *\\n");printf("* 2. Delete by tele  *\\n");printf("* 0. EXIT DELETE     *\\n");printf("------------------------------\\n");
}static void DeleContact(Contacts* con, int (*Find)(Contacts*, const char*))
{assert(con);char temp[NAME_MAX + TELE_MAX] = { 0 };scanf("%s", temp);int pos = Find(con, temp);if (pos == -1){system("cls");PrintContact(con);printf(RED"err:Nonexistence!!!\\n"NONE);return;}void* dest = (void*)(&con->data[pos]);void* scr = (void*)(&con->data[pos + 1]);int num = sizeof(PeoInfo) * (con->sz - 1 - pos);memmove(dest, scr, num);con->sz--;system("cls");PrintContact(con);printf(GREEN"   Deleted successfully!\\n"NONE);system("pause");
}
void DeleContactMenu(Contacts* con)
{while (getchar() != '\\n');assert(con);system("cls");PrintContact(con);if (con->sz == 0){printf(RED"err:The contact is empty and cannot be deleted!!!"NONE);return;}char input = 0;do{deletmenu();printf(BLUE"Please select:>"NONE);scanf("%c", &input);switch (input - '0'){case 1:printf(BLUE"Please enter the name of the person you want to delete:>"NONE);DeleContact(con, FindByName);break;case 2:printf(BLUE"Please enter the telephone of the person you want to delete:>"NONE);DeleContact(con, FindByTele);break;case 0:puts(RED"	Exit successfully\\n"RED);system("pause");return;default:printf(RED"err:Incorrect input:please enter again!\\n\\n"NONE);system("pause");break;}if (input != '\\n'){while (getchar() != '\\n');}} while (1);
}

📱3.6 通讯录查找功能的实现

打印查找菜单——通过名字或者电话号进行查找

可以实现为回调函数,及函数指针传参,通过参数相同,返回值相同,意义相同,便于添加查找方式

static void searchmenu()
{printf("------------------------------\\n");printf("* 1. Search by name  *\\n");printf("* 2. Search by tele  *\\n");printf("* 0. EXIT Search     *\\n");printf("------------------------------\\n");
}
static void SearchContact(Contacts* con, int (*Find)(Contacts*, const char*))
{assert(con);char temp[NAME_MAX + TELE_MAX] = { 0 };scanf("%s", temp);int pos = Find(con, temp);if (pos == -1){system("cls");PrintContact(con);printf(RED"err:Nonexistence!!!\\n"NONE);return;}system("cls");printf(GREEN"Sucess:Search successfully!!!\\n"NONE);printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5s|%-5s|%-12s|%-30s|\\n", "姓名", "年龄", "性别", "电话号", "住址");printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5d|%-5s|%-12s|%-30s|\\n",con->data[pos].name,con->data[pos].age, con->data[pos].sex,con->data[pos].tele, con->data[pos].addr);printf("|--------------------|-----|-----|------------|------------------------------|\\n");system("pause");return;
}void SearhConMenu(Contacts* con)
{while (getchar() != '\\n');assert(con);system("cls");PrintContact(con);if (con->sz == 0){printf(RED"err:The contact is empty and cannot be deleted!!!"NONE);return;}char input = 0;do{searchmenu();printf(BLUE"Please select:>"NONE);scanf("%c", &input);switch (input - '0'){case 1:printf(BLUE"Please enter the name of the person you want to search:>"NONE);SearchContact(con, FindByName);break;case 2:printf(BLUE"Please enter the telephone of the person you want to search:>"NONE);SearchContact(con, FindByTele);break;case 0:puts(GREEN"	Exit successfully\\n"RED);system("pause");return;default:printf(RED"err:Incorrect input:please enter again!\\n\\n"NONE);system("pause");break;}if (input != '\\n'){while (getchar() != '\\n');}} while (1);
}

📱3.7 通讯录排序功能的实现

通过回调函数,使用库函数qsort,通过名字进行排序,排序完成,打印通讯录。

//排序通讯录信息
int cmp_by_name(const void* elem1, const void* elem2)
{return strcmp(((PeoInfo*)elem1)->name, ((PeoInfo*)elem2)->name);
}
void QsortCon(Contacts* con)
{assert(con);system("cls");int ret = sizeof(con->data[0]);qsort(con->data, con->sz, sizeof(con->data[0]), cmp_by_name);PrintContact(con);printf(GREEN"Sort completion!\\n"NONE);system("pause");
}

📱3.8 通讯录修改功能的实现

通过名字查找需要修改数据的位置,进而进行直接写入

void ModifyCon(Contacts* con)
{assert(con);PrintContact(con);char name[NAME_MAX] = { 0 };printf(BLUE"Please enter the name of the person whose information you want to change:>"NONE);scanf("%s", name);int pos = FindByName(con, name);if (pos == -1){printf(RED"err:The changed object does not exist!!!\\n"NONE);return;}else{system("cls");printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5s|%-5s|%-12s|%-30s|\\n", "姓名", "年龄", "性别", "电话号", "住址");printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5d|%-5s|%-12s|%-30s|\\n",con->data[pos].name,con->data[pos].age, con->data[pos].sex,con->data[pos].tele, con->data[pos].addr);printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("Please enter the change information!\\n");printf(BLUE"Please enter name:>"NONE);scanf("%s", con->data[pos].name);printf(BLUE"Please enter age:>"NONE);scanf("%d", &(con->data[pos].age));printf(BLUE"Please enter gender:>"NONE);scanf("%s", con->data[pos].sex);printf(BLUE"Please enter telephone number:>"NONE);scanf("%s", con->data[pos].tele);printf(BLUE"Please enter the address:>"NONE);scanf("%s", con->data[pos].addr);printf(GREEN"Information change completed!\\n"NONE);system("pause");}
}

📱3.9 通讯录打印功能的实现

void PrintContact(const Contacts* con)
{assert(con);int i = 0;printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5s|%-5s|%-12s|%-30s|\\n", "姓名", "年龄", "性别", "电话号", "住址");for (i = 0; i < con->sz; i++){printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5d|%-5s|%-12s|%-30s|\\n",(con->data + i)->name,con->data[i].age, con->data[i].sex,con->data[i].tele, con->data[i].addr);}printf("|--------------------|-----|-----|------------|------------------------------|\\n");
}

📱3.10 通讯录文件保存及释放空间

通过文件操作,保存所有数据到contacts.dat二进制文件中,并将堆区所动态开辟的空间进行释放.

//销毁通讯录
void DestroyContact(Contacts* con)
{assert(con);if (con->data != NULL){free(con->data);con->data = NULL;con->capacity = con->sz = 0;}
}//保存通讯录信息到文件
void SaveContact(const Contacts* con)
{FILE* pf = fopen("contacts.dat", "wb");if (pf == NULL){perror("SaveContact::fopen");return;}//写文件int i = 0;for (i = 0; i < con->sz; i++){fwrite(con->data + i, sizeof(PeoInfo), 1, pf);}//关闭文件fclose(pf);pf = NULL;
}

📰4. 通讯录完整源代码:

运行图:

 

📱4.1 Contacts.h

#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#define RED "\\033[0;32;31m"
#define GREEN "\\033[0;32;32m"
#define BLUE  "\\033[0;32;34m"
#define NONE "\\033[m"#define	NAME_MAX 20
#define PASSWD_MAX 12#define	SEX_MAX 5
#define TELE_MAX 12
#define	ADDR_MAX 30#define DEFAULT_SZ 4//初始化容量大小
enum Option1
{EXIT1,LOGOIN,SIGNIN,
};
typedef struct Users
{char UserName[NAME_MAX];char UserPasswd[PASSWD_MAX];
}Users;enum Option2
{EXIT2,ADD,DEL,SEARCH,MODIFY,SORT,PRINT
};
typedef struct PeoInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;
typedef struct Contacts
{PeoInfo* data;int sz;int capacity;
}Contacts;bool UserLogo();
void UserSign();void InitContact(Contacts* con);
void AddContact(Contacts* con);
void DeleContactMenu(Contacts* con);
void PrintContact(const Contacts* con);
void SearhConMenu(Contacts* con);
void QsortCon(Contacts* con);
void ModifyCon(Contacts* con);
void SaveContact(const Contacts* con);
void DestroyContact(Contacts* con);

📱4.2 Contacts.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Contacts.h"
static void check_capacity(Contacts* con)
{if (con->sz == con->capacity){PeoInfo* tmp = (PeoInfo*)realloc(con->data, con->capacity == 0 ? DEFAULT_SZ * sizeof(PeoInfo) : (con->capacity + 2) * sizeof(PeoInfo));if (tmp != NULL){con->data = tmp;}else{perror("check_capacity::realloc");return;}if (con->capacity == 0){con->capacity = 4;}else{con->capacity += 2;}}return;
}static void LoadContact(Contacts* con)
{FILE* pf = fopen("contacts.dat", "rb");if (pf == NULL){perror("LoadContact::fopen");return;}PeoInfo tmp = { 0 };while (fread(&tmp, sizeof(PeoInfo), 1, pf)){check_capacity(con);con->data[con->sz] = tmp;con->sz++;}fclose(pf);pf = NULL;
}void InitContact(Contacts* con)
{assert(con);con->sz = con->capacity = 0;con->data = NULL;LoadContact(con);
}void AddContact(Contacts* con)
{while (getchar() != '\\n');check_capacity(con);system("cls");printf("Add contact information\\n");printf(BLUE"Please enter name:>"NONE);scanf("%s", con->data[con->sz].name);printf(BLUE"Please enter age:>"NONE);scanf("%d", &(con->data[con->sz].age));printf(BLUE"Please enter gender:>"NONE);scanf("%s", con->data[con->sz].sex);printf(BLUE"Please enter telephone number:>"NONE);scanf("%s", con->data[con->sz].tele);printf(BLUE"Please enter the address:>"NONE);scanf("%s", con->data[con->sz].addr);con->sz++;printf(GREEN"  Add successfully\\n"NONE);system("pause");
}//打印通讯录信息,只打印,不修改
void PrintContact(const Contacts* con)
{assert(con);int i = 0;printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5s|%-5s|%-12s|%-30s|\\n", "姓名", "年龄", "性别", "电话号", "住址");for (i = 0; i < con->sz; i++){printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5d|%-5s|%-12s|%-30s|\\n",(con->data + i)->name,con->data[i].age, con->data[i].sex,con->data[i].tele, con->data[i].addr);}printf("|--------------------|-----|-----|------------|------------------------------|\\n");
}
static int FindByName(Contacts* con, const char* name)
{assert(con && name);for (int i = 0; i < con->sz; ++i){if (0 == strcmp(con->data[i].name, name)){return i;}}return -1;
}
static int FindByTele(Contacts* con, const char* tele)
{assert(con && tele);for (int i = 0; i < con->sz; ++i){if (0 == strcmp(con->data[i].tele, tele)){return i;}}return -1;
}
static void deletmenu()
{printf("------------------------------\\n");printf("* 1. Delete by name  *\\n");printf("* 2. Delete by tele  *\\n");printf("* 0. EXIT DELETE     *\\n");printf("------------------------------\\n");
}static void DeleContact(Contacts* con, int (*Find)(Contacts*, const char*))
{assert(con);char temp[NAME_MAX + TELE_MAX] = { 0 };scanf("%s", temp);int pos = Find(con, temp);if (pos == -1){system("cls");PrintContact(con);printf(RED"err:Nonexistence!!!\\n"NONE);return;}void* dest = (void*)(&con->data[pos]);void* scr = (void*)(&con->data[pos + 1]);int num = sizeof(PeoInfo) * (con->sz - 1 - pos);memmove(dest, scr, num);con->sz--;system("cls");PrintContact(con);printf(GREEN"   Deleted successfully!\\n"NONE);system("pause");return;
}
void DeleContactMenu(Contacts* con)
{while (getchar() != '\\n');assert(con);system("cls");PrintContact(con);if (con->sz == 0){printf(RED"err:The contact is empty and cannot be deleted!!!"NONE);return;}char input = 0;do{deletmenu();printf(BLUE"Please select:>"NONE);scanf("%c", &input);switch (input - '0'){case 1:printf(BLUE"Please enter the name of the person you want to delete:>"NONE);DeleContact(con, FindByName);break;case 2:printf(BLUE"Please enter the telephone of the person you want to delete:>"NONE);DeleContact(con, FindByTele);break;case 0:puts(GREEN"	Exit successfully\\n"NONE);system("pause");return;default:printf(RED"err:Incorrect input:please enter again!\\n\\n"NONE);system("pause");break;}if (input != '\\n'){while (getchar() != '\\n');}} while (1);
}static void searchmenu()
{printf("------------------------------\\n");printf("* 1. Search by name  *\\n");printf("* 2. Search by tele  *\\n");printf("* 0. EXIT Search     *\\n");printf("------------------------------\\n");
}
static void SearchContact(Contacts* con, int (*Find)(Contacts*, const char*))
{assert(con);char temp[NAME_MAX + TELE_MAX] = { 0 };scanf("%s", temp);int pos = Find(con, temp);if (pos == -1){system("cls");PrintContact(con);printf(RED"err:Nonexistence!!!\\n"NONE);return;}system("cls");printf(GREEN"Sucess:Search successfully!!!\\n"NONE);printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5s|%-5s|%-12s|%-30s|\\n", "姓名", "年龄", "性别", "电话号", "住址");printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5d|%-5s|%-12s|%-30s|\\n",con->data[pos].name,con->data[pos].age, con->data[pos].sex,con->data[pos].tele, con->data[pos].addr);printf("|--------------------|-----|-----|------------|------------------------------|\\n");system("pause");return;
}void SearhConMenu(Contacts* con)
{while (getchar() != '\\n');assert(con);system("cls");PrintContact(con);if (con->sz == 0){printf(RED"err:The contact is empty and cannot be deleted!!!"NONE);return;}char input = 0;do{searchmenu();printf(BLUE"Please select:>"NONE);scanf("%c", &input);switch (input - '0'){case 1:printf(BLUE"Please enter the name of the person you want to search:>"NONE);SearchContact(con, FindByName);break;case 2:printf(BLUE"Please enter the telephone of the person you want to search:>"NONE);SearchContact(con, FindByTele);break;case 0:puts(GREEN"	Exit successfully\\n"RED);system("pause");return;default:printf(RED"err:Incorrect input:please enter again!\\n\\n"NONE);system("pause");break;}if (input != '\\n'){while (getchar() != '\\n');}} while (1);
}//排序通讯录信息
int cmp_by_name(const void* elem1, const void* elem2)
{return strcmp(((PeoInfo*)elem1)->name, ((PeoInfo*)elem2)->name);
}
void QsortCon(Contacts* con)
{assert(con);system("cls");int ret = sizeof(con->data[0]);qsort(con->data, con->sz, sizeof(con->data[0]), cmp_by_name);PrintContact(con);printf(GREEN"Sort completion!\\n"NONE);system("pause");
}void ModifyCon(Contacts* con)
{assert(con);PrintContact(con);char name[NAME_MAX] = { 0 };printf(BLUE"Please enter the name of the person whose information you want to change:>"NONE);scanf("%s", name);int pos = FindByName(con, name);if (pos == -1){printf(RED"err:The changed object does not exist!!!\\n"NONE);return;}else{system("cls");printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5s|%-5s|%-12s|%-30s|\\n", "姓名", "年龄", "性别", "电话号", "住址");printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("|%-20s|%-5d|%-5s|%-12s|%-30s|\\n",con->data[pos].name,con->data[pos].age, con->data[pos].sex,con->data[pos].tele, con->data[pos].addr);printf("|--------------------|-----|-----|------------|------------------------------|\\n");printf("Please enter the change information!\\n");printf(BLUE"Please enter name:>"NONE);scanf("%s", con->data[pos].name);printf(BLUE"Please enter age:>"NONE);scanf("%d", &(con->data[pos].age));printf(BLUE"Please enter gender:>"NONE);scanf("%s", con->data[pos].sex);printf(BLUE"Please enter telephone number:>"NONE);scanf("%s", con->data[pos].tele);printf(BLUE"Please enter the address:>"NONE);scanf("%s", con->data[pos].addr);printf(GREEN"Information change completed!\\n"NONE);system("pause");}
}//销毁通讯录
void DestroyContact(Contacts* con)
{assert(con);if (con->data != NULL){free(con->data);con->data = NULL;con->capacity = con->sz = 0;}
}//保存通讯录信息到文件
void SaveContact(const Contacts* con)
{FILE* pf = fopen("contacts.dat", "wb");if (pf == NULL){perror("SaveContact::fopen");return;}//写文件int i = 0;for (i = 0; i < con->sz; i++){fwrite(con->data + i, sizeof(PeoInfo), 1, pf);}//关闭文件fclose(pf);pf = NULL;
}

📱4.3 ContactsUsers.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Contacts.h"bool UserLogo()
{FILE* pf = fopen("Users.bat", "rb");Users user1 = { 0,0 }, user2 = { 0,0 };if (pf == NULL){printf(RED"err:The user directory file does not exist!\\n"NONE"  Please register the user first\\n"NONE);system("pause");return false;}printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf("          Login interface\\n");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf(BLUE"Please enter the account number:>"NONE);if (scanf("%s", user1.UserName) == EOF){perror("Incorrect input");}fread(&user2, sizeof(Users), 1, pf);while (1){if (strcmp(user1.UserName, user2.UserName) != 0){if (feof(pf) == 0)//未到文件尾 fread(&user2, sizeof(Users), 1, pf);else//到了文件尾仍然没有相同的字符串-说明输入的账号新的 可以去注册界面 {printf(GREEN"The account has not been registered. Rejump\\n"NONE);system("pause");fclose(pf);pf = NULL;return false;//利用break来跳出无限循环 }}else{break;}}printf(BLUE"Please enter password:>"NONE);int n = 3;//三次输入机会while (n){if (scanf("%s", user1.UserPasswd) == EOF){perror("Incorrect input");}if (strcmp(user1.UserPasswd, user2.UserPasswd) != 0){if (n == 1){printf(RED"	 err:Login failure\\n"NONE);fclose(pf);pf = NULL;system("pause");return false;}printf(RED"err:Password error\\n"NONEBLUE"Please enter the password again:>"NONE);}else{break;}n--;}printf(GREEN"    Login success!\\n"NONE);getchar();system("pause");fclose(pf); pf = NULL;return true;
}void UserSign()
{Users user1 = { 0,0 },user2 = {0,0};FILE* pf,*PF = NULL;pf = fopen("Users.bat", "rb");if (pf == NULL){PF = fopen("Users.bat", "wb");fclose(PF);;}pf = fopen("Users.bat", "rb");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf("      Registration interface\\n");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf(BLUE"Please set a new user name:>"NONE);if (scanf("%s", user1.UserName) == EOF){perror("Incorrect input");}printf(GREEN"       Enter successfully\\n"NONE);fread(&user2, sizeof(Users), 1, pf);//【判断】有没有注册过-比较字符串是否相等 
//不相等->是否到文件尾 while (1){if (strcmp(user1.UserName, user2.UserName) != 0){if (feof(pf) == 0)//未到文件尾 fread(&user2, sizeof(Users), 1, pf);else//到了文件尾仍然没有相同的字符串-说明输入的账号使新的 可以去注册界面 {printf(GREEN"The account has not been registered. Rejump\\n"NONE);break;//利用break来跳出无限循环 }}else{printf(RED"err: The account has been registered!\\n"NONE);fclose(pf); pf = NULL;system("pause");return;}}//判断两次密码是否相同,且必须大于5位printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf("      Registration interface       \\n");printf("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\\n");printf(BLUE"Please set the user password :(English and digital only):>"NONE);if (scanf("%s", user1.UserPasswd) == EOF){perror("Incorrect input");}while (strlen(user1.UserPasswd) < 5){printf(RED"err:The password must be at least six characters!"NONEBLUE"\\nPlease enter the password again:>"NONE);if (scanf("%s", user1.UserPasswd) == EOF){perror("Incorrect input");}}printf(BLUE"Please enter your password again:>"NONE);char tmp[PASSWD_MAX] = { 0 };do {if (EOF == scanf("%s", tmp)){perror("Incorrect input");}if (strcmp(tmp, user1.UserPasswd) != 0)printf(RED"\\nerr:The two passwords are different.\\\\nPlease enter the password again->"NONE);else break;} while (1);//两次密码一致fclose(pf);	pf = NULL;pf = fopen("Users.bat", "ab");if (pf == NULL){perror("UserSign failed");}//fwrite会在当前文件指针的位置写入数据//"w" 打开,文件指针指到头,只写;"a" 打开,指向文件尾else{fwrite(&user1, sizeof(Users), 1, pf);printf(GREEN"     Registered successfully!\\n"NONE);system("pause");fclose(pf);pf = NULL;}return;
}

📱4.4 Contacts_Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Contacts.h"void menu2()
{printf("------------------------------\\n");printf("---------- CONTACT -----------\\n");printf("* 1.add       2.del  *\\n");printf("\\n");printf("* 3.search  4.modify *\\n");printf("\\n");printf("* 5.sort    6.print  *\\n");printf("(0.exit)\\n");printf("------------------------------\\n");}
void test2()
{char input = 0;Contacts con;InitContact(&con);while (1){system("cls");menu2();printf(BLUE"Welcome to use,please select:>"NONE);if (scanf("%c", &input) != EOF){switch (input - '0'){case ADD:AddContact(&con);break;case DEL:DeleContactMenu(&con);break;case SEARCH:SearhConMenu(&con);break;case MODIFY:ModifyCon(&con);break;case SORT:QsortCon(&con);break;case PRINT:PrintContact(&con);system("pause");break;case EXIT2:SaveContact(&con);DestroyContact(&con);puts(RED"	Exit successfully\\n"RED);system("pause");return;default:printf(RED"err:Incorrect input:please enter again!\\n\\n"NONE);system("pause");break;}if (input != '\\n'){while (getchar() != '\\n');}}}
}void menu1()
{printf("------------------------------\\n");printf("---------- CONTACT -----------\\n");printf("  1.Logo In  *\\n");printf("\\n");printf("  2.Sign In  *\\n");printf("\\n");printf("*(0.exit)*\\n");printf("------------------------------\\n");
}
void test1()
{char input;do{system("cls");menu1();printf(BLUE"Welcome to use,please select:>"NONE);if (scanf("%c", &input) != EOF){switch (input-'0'){case LOGOIN:system("cls"); if (UserLogo() == true){test2();}break;case SIGNIN:system("cls"); UserSign(); break;case EXIT1:puts(RED"	Exit successfully\\n"RED); exit(0);default:printf(RED"err:Incorrect input:please enter again!\\n\\n"NONE);system("pause");break;}if (input != '\\n'){while (getchar() != '\\n');}}} while (1);
}int main()
{test1();return 0;
}