目录
📰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自带画图板
采取工程内多文件进行链接编译,包括:
- 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!"NONE BLUE"\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"NONE BLUE"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"NONE BLUE"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!"NONE BLUE"\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; }