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!"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. 通讯录模块功能分析实现:

具体实现功能:

  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"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;
}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值