C语言小项目 -- 通讯录完整代码(登陆系统+动态开辟 + 文件操作)
目录
📰0. 项目介绍
📰1. 开发环境及框架
📱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自带画图板
采取工程内多文件进行链接编译,包括:
- Contacts.h——头文件,所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件。
- Contacts.c——源文件,里边含通讯录各种操作函数的具体功能实现
- ContactsUsers.c——源文件,包含账户登录注册功能的具体实现
- Contacts_Test.c——测试通讯录功能的使用,包含主体运行,主函数等
📰2. 通讯录账户模块功能分析实现:
具体实现功能:
- 用户菜单界面的实现
- 用户数据结构设计
- 用户注册功能的实现(通过文件操作将数据写入文件)
- 用户登录功能的实现(通过文件操作将数据从文件读出)
📱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. 通讯录模块功能分析实现:
具体实现功能:
- 通讯录菜单界面的实现
- 通讯录数据结构设计
- 通讯录文件加载的实现(通过文件操作将数据从文件读出)
- 通讯录的初始化,以及动态增长
- 通讯录添加功能的实现(通过文件操作将数据写入文件)
- 通讯录删除功能的实现(通过文件操作将数据从文件读出)
- 通讯录查找功能的实现(通过文件操作将数据读出,用名字或者手机号进行查找)
- 通讯录修改功能的实现(通过文件操作将数据写入文件)
- 通讯录排序功能的实现
- 通讯录的打印(通过文件操作将数据从文件读出)
- 保存通讯录到文件并释放空间(通过文件操作将数据写入文件)
📱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; }