程序设计:小学100以内正整数加减运算

1、课题内容

本课题的核心是设计一个“小学100以内整数加减运算”系统,该系统需要满足以下主要功能

(1)题目生成:随机生成10道不重复的加减运算题,减法运算确保被减数大于减数。

提供选择题和填空题两种出题方式供用户选择。

(2)答题与评分:用户答题后,系统根据答案计算得分,并累计总分。提供用户查看错题的功能,并在用户答对错题后将其从错题集中删除。

(3)用户交互与反馈:提供菜单选项,包括登录、答题、查阅答题情况、错题再答等。答题结束后,系统给出总用时。

(4)文件导出功能:提供生成题目及对应答案到文本文件的功能,以便线下测试。

2、功能要求

(1)菜单系统:设计清晰易用的菜单界面,提供用户选择不同功能的入口。

(2)答题情况查阅:答题期间不允许查阅答题情况,答题结束后可以查看。

(3)错题再答:提供错题再答功能,答对后从错题集中删除。

(4)文件导出:允许用户选择文件所在地址,生成题目和答案文本文件。

3、选做要求

(1)帮助功能:提供系统使用说明和模拟答题功能,帮助用户了解系统操作。

(2)用户管理:设计完整的登录、注册以及找回密码功能。

(3)题目难度与数量:允许用户自主选择题目难度和数量。

4、其他要求

(1)界面与交互:界面设计应美观,用户交互应方便快捷。

(2)代码注释:代码应包含详细的注释,包括变量、函数以及关键语句段的解释。

(3)程序结构:程序结构应清晰,可读性强,命名规范。

本课题旨在设计一个功能齐全、界面美观、交互便捷的小学100以内整数加减运算系统,旨在帮助小学生提高数学运算能力,并通过各种功能提供个性化的学习体验。

#define _CRT_SECURE_NO_WARNINGS  //在VS中使用 避免出现scanf printf不安全警告

/*********头文件************/
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#include <errno.h>
#include<time.h>
#include<windows.h>
#include<conio.h>

/*********宏定义************/
#define MAX_NUM 99//困难模式出的题目数字范围 x + y = z  x - y = z  0<x,y,z <= MAX_NUM
#define MAX_NUM1 9//简单模式出的题目数字范围 x + y = z  x - y = z  0<x,y,z <= MAX_NUM
#define MAX_NUM2 19//中等模式出的题目数字范围 x + y = z  x - y = z  0<x,y,z <= MAX_NUM
#define MAX_ID 11//ID不超过11位
#define MAX_PWD 20//密码不超过20位
#define MAX_NAME 15//姓名不超过15位
#define MAX_SEX 5//性别不超过5位
#define MAX_PHONE 12//手机号不超过12位

/*********题目结构体************/
typedef struct _Title
{
	int opt_num1;//操作数1
	int opt_num2;//操作数2
	int opt_ch;//操作符0代表+   1代表-
	int style;//0代表选择题     1代表填空题
	int correct_ans;//如果是选择保存的是正确选项 如果是填空保存正确答案
	int user_ans;//记录用户的答案
	int choice[4];//随机生成的4个答案 不重复 其中一个为正确答案
}Title;

/*******用户结构体*******/
typedef struct Users
{
	char id[MAX_ID];//用户ID
	char pwd[MAX_PWD];//用户密码
	char name[MAX_NAME];//用户姓名
	char sex[MAX_SEX];//用户性别
	char phone[MAX_PHONE];//用户手机号
}Users;

/******全局变量*******/
char ID[MAX_ID];//登陆时所用ID
int TITLE_NUM;//每次测试的出题量

/******自定义字体和背景颜色******/
/* uFore:字体; uBack:背景 */
VOID SetColor(UINT uFore, UINT uBack) {
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, uFore + uBack * 0x10);
}

/********主菜单*********/
/*choice记录用户的选择*/
/*返回值:返回用户的选择*/
int Main_Menu()
{
	int choice;//用户选择
	system("cls");//清空屏幕输出
	printf("1:登录答题\n");
	printf("2:查阅答题\n");
	printf("3:错题再答\n");
	printf("4:打印试卷\n");
	printf("5:系统帮助\n");
	printf("0:退出系统\n");
	printf("请输入选项:");
	while (1)
	{
		SetColor(4, 15);
		if (scanf("%d", &choice) == 1)
		{
			SetColor(0, 15);
			if (choice >= 0 && choice <= 5)
			{
				break;
			}
			else
			{
				while (getchar() != '\n');
				printf("请重新输入选项:");
			}
		}
		else
		{
			SetColor(0, 15);
			while (getchar() != '\n');
			printf("请重新输入选项:");
		}
	}
	return choice;//返回选择
}

/**********生成简单难度选择题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 已答题的数量,即数组当前长度*/
void Creat_Choice_Title1(Title title[], int n)
{
	int i, j;//循环迭代使用
	title[n].style = 0;//题目类型为选择题
	do
	{
		title[n].opt_num1 = (rand() % MAX_NUM1) + 1;//随机生成操作数1
		title[n].opt_num2 = (rand() % MAX_NUM1) + 1;//随机生成操作数2
		title[n].opt_ch = rand() % 2;//随机生成操作符
		switch (title[n].opt_ch)//根据操作符 生成正确答案等
		{
		case 0://加法的情况
			if (title[n].opt_num1 + title[n].opt_num2 <= MAX_NUM1)//确保结果在100以内
			{
				title[n].correct_ans = title[n].opt_num1 + title[n].opt_num2;//得到正确答案
				for (i = 0; i < 4; i++)//随机生成4个选项
				{
					title[n].choice[i] = (rand() % MAX_NUM1) + 1;
					while (title[n].choice[i] == title[n].correct_ans)//确保随机生成的选项与答案不重复
					{
						title[n].choice[i] = (rand() % MAX_NUM1) + 1;//重复就重新生成
					}
					for (j = 0; j < i; j++)//确保和之前已经生成的选项不重复
					{
						if (title[n].choice[i] == title[n].choice[j])//重复也重新生成
						{
							i--;
							break;
						}
					}
				}
				title[n].correct_ans = rand() % 4;//随机选择一个选项作为正确答案
				title[n].choice[title[n].correct_ans] = title[n].opt_num1 + title[n].opt_num2;//把答案放在正确选项的位置
				return;
			}
			break;
		case 1://减法的情况
			if (title[n].opt_num1 - title[n].opt_num2 > 0)//确保答案大于0
			{
				title[n].correct_ans = title[n].opt_num1 - title[n].opt_num2;//获取正确答案
				for (i = 0; i < 4; i++)//随机生成4个选项
				{
					title[n].choice[i] = (rand() % MAX_NUM1) + 1;
					while (title[n].choice[i] == title[n].correct_ans)//确保随机生成的选项与答案不重复
					{
						title[n].choice[i] = (rand() % MAX_NUM1) + 1;//重复就重新生成
					}
					for (j = 0; j < i; j++)//确保和之前已经生成的选项不重复
					{
						if (title[n].choice[i] == title[n].choice[j])//重复也重新生成
						{
							i--;
							break;
						}
					}
				}
				title[n].correct_ans = rand() % 4;//随机选择一个选项作为正确答案
				title[n].choice[title[n].correct_ans] = title[n].opt_num1 - title[n].opt_num2;//把答案放在正确选项的位置
				return;
			}
			break;
		}
	} while (1);
}

/**********生成中等难度选择题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 已答题的数量,即数组当前长度*/
void Creat_Choice_Title2(Title title[], int n)
{
	int i, j;//循环迭代使用
	title[n].style = 0;//题目类型为选择题
	do
	{
		title[n].opt_num1 = (rand() % MAX_NUM2) + 1;//随机生成操作数1
		title[n].opt_num2 = (rand() % MAX_NUM2) + 1;//随机生成操作数2
		title[n].opt_ch = rand() % 2;//随机生成操作符
		switch (title[n].opt_ch)//根据操作符 生成正确答案等
		{
		case 0://加法的情况
			if (title[n].opt_num1 + title[n].opt_num2 <= MAX_NUM2)//确保结果在100以内
			{
				title[n].correct_ans = title[n].opt_num1 + title[n].opt_num2;//得到正确答案
				for (i = 0; i < 4; i++)//随机生成4个选项
				{
					title[n].choice[i] = (rand() % MAX_NUM2) + 1;
					while (title[n].choice[i] == title[n].correct_ans)//确保随机生成的选项与答案不重复
					{
						title[n].choice[i] = (rand() % MAX_NUM2) + 1;//重复就重新生成
					}
					for (j = 0; j < i; j++)//确保和之前已经生成的选项不重复
					{
						if (title[n].choice[i] == title[n].choice[j])//重复也重新生成
						{
							i--;
							break;
						}
					}
				}
				title[n].correct_ans = rand() % 4;//随机选择一个选项作为正确答案
				title[n].choice[title[n].correct_ans] = title[n].opt_num1 + title[n].opt_num2;//把答案放在正确选项的位置
				return;
			}
			break;
		case 1://减法的情况
			if (title[n].opt_num1 - title[n].opt_num2 > 0)//确保答案大于0
			{
				title[n].correct_ans = title[n].opt_num1 - title[n].opt_num2;//获取正确答案
				for (i = 0; i < 4; i++)//随机生成4个选项
				{
					title[n].choice[i] = (rand() % MAX_NUM2) + 1;
					while (title[n].choice[i] == title[n].correct_ans)//确保随机生成的选项与答案不重复
					{
						title[n].choice[i] = (rand() % MAX_NUM2) + 1;//重复就重新生成
					}
					for (j = 0; j < i; j++)//确保和之前已经生成的选项不重复
					{
						if (title[n].choice[i] == title[n].choice[j])//重复也重新生成
						{
							i--;
							break;
						}
					}
				}
				title[n].correct_ans = rand() % 4;//随机选择一个选项作为正确答案
				title[n].choice[title[n].correct_ans] = title[n].opt_num1 - title[n].opt_num2;//把答案放在正确选项的位置
				return;
			}
			break;
		}
	} while (1);
}

/**********生成困难难度选择题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 已答题的数量,即数组当前长度*/
void Creat_Choice_Title3(Title title[], int n)
{
	int i, j;//循环迭代使用
	title[n].style = 0;//题目类型为选择题
	do
	{
		title[n].opt_num1 = (rand() % MAX_NUM) + 1;//随机生成操作数1
		title[n].opt_num2 = (rand() % MAX_NUM) + 1;//随机生成操作数2
		title[n].opt_ch = rand() % 2;//随机生成操作符
		switch (title[n].opt_ch)//根据操作符 生成正确答案等
		{
		case 0://加法的情况
			if (title[n].opt_num1 + title[n].opt_num2 <= MAX_NUM)//确保结果在100以内
			{
				title[n].correct_ans = title[n].opt_num1 + title[n].opt_num2;//得到正确答案
				for (i = 0; i < 4; i++)//随机生成4个选项
				{
					title[n].choice[i] = (rand() % MAX_NUM) + 1;
					while (title[n].choice[i] == title[n].correct_ans)//确保随机生成的选项与答案不重复
					{
						title[n].choice[i] = (rand() % MAX_NUM) + 1;//重复就重新生成
					}
					for (j = 0; j < i; j++)//确保和之前已经生成的选项不重复
					{
						if (title[n].choice[i] == title[n].choice[j])//重复也重新生成
						{
							i--;
							break;
						}
					}
				}
				title[n].correct_ans = rand() % 4;//随机选择一个选项作为正确答案
				title[n].choice[title[n].correct_ans] = title[n].opt_num1 + title[n].opt_num2;//把答案放在正确选项的位置
				return;
			}
			break;
		case 1://减法的情况
			if (title[n].opt_num1 - title[n].opt_num2 > 0)//确保答案大于0
			{
				title[n].correct_ans = title[n].opt_num1 - title[n].opt_num2;//获取正确答案
				for (i = 0; i < 4; i++)//随机生成4个选项
				{
					title[n].choice[i] = (rand() % MAX_NUM) + 1;
					while (title[n].choice[i] == title[n].correct_ans)//确保随机生成的选项与答案不重复
					{
						title[n].choice[i] = (rand() % MAX_NUM) + 1;//重复就重新生成
					}
					for (j = 0; j < i; j++)//确保和之前已经生成的选项不重复
					{
						if (title[n].choice[i] == title[n].choice[j])//重复也重新生成
						{
							i--;
							break;
						}
					}
				}
				title[n].correct_ans = rand() % 4;//随机选择一个选项作为正确答案
				title[n].choice[title[n].correct_ans] = title[n].opt_num1 - title[n].opt_num2;//把答案放在正确选项的位置
				return;
			}
			break;
		}
	} while (1);
}

/**********选择题获取用户的答案*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 即数组上次测试答题后的长度*/
/*参数3 int i: 当前测试的第i+1题*/
/*返回值:0代表该题答错  1代表回答正确*/
int Get_User_Choice(Title title[], int n, int i)
{
	int j;//用于迭代
	char choice;//获取 用户选择
	system("cls");//清屏
	printf("第%d题: ", n + i + 1);//输出题号
	printf("%d ", title[n + i].opt_num1);//输出操作数1
	switch (title[n + i].opt_ch)//输出符号
	{
	case 0:printf("+ "); break;
	case 1:printf("- "); break;
	}
	printf("%d = \n", title[n + i].opt_num2);//输出操作数2
	for (j = 0; j < 4; j++)//打印4个选项
	{
		printf("%c:%d    ", 'A' + j, title[n + i].choice[j]);
	}
	printf("\n");
	printf("请输入选择:");
	while (getchar() != '\n');//清空缓冲区
	SetColor(4, 15);
	scanf("%c", &choice);//获取选择
	SetColor(0, 15);
	choice = toupper(choice);//转换成大写
	while (choice < 'A' || choice > 'D')//判断输入数据的范围 不在A--D则重新输入
	{
		printf("请重新输入选择:");
		fflush(stdin);
		SetColor(4, 15);
		scanf("%c", &choice);
		SetColor(0, 15);
		choice = toupper(choice);
	}
	title[n + i].user_ans = choice - 'A';//记录用户的选择
	if (title[n + i].user_ans == title[n + i].correct_ans)//如果答对了
	{
		printf("恭喜你,答对了!\n");
		system("pause");
		return 1;
	}
	printf("答错了,正确答案为:%c!\n", title[n + i].correct_ans + 'A');//如果答错了
	system("pause");
	return 0;
}

/***错题重答中选择题获取用户的答案***/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int i: 当前测试的第i+1题*/
/*返回值:0代表该题答错  1代表回答正确*/
int Get_User_Choice2(Title title[], int n, int i)
{
	int j;//用于迭代
	char choice;//获取 用户选择
	system("cls");//清屏
	printf("第%d题: ", i + 1);//输出题号
	printf("%d ", title[i].opt_num1);//输出操作数1
	switch (title[i].opt_ch)//输出符号
	{
	case 0:printf("+ "); break;
	case 1:printf("- "); break;
	}
	printf("%d = \n", title[i].opt_num2);//输出操作数2
	for (j = 0; j < 4; j++)//打印4个选项
	{
		printf("%c:%d    ", 'A' + j, title[i].choice[j]);
	}
	printf("\n");
	printf("请输入选择:");
	while (getchar() != '\n');//清空缓冲区
	SetColor(4, 15);
	scanf("%c", &choice);//获取选择
	SetColor(0, 15);
	choice = toupper(choice);//转换成大写
	while (choice < 'A' || choice > 'D')//判断输入数据的范围 不在A--D则重新输入
	{
		printf("请重新输入选择:");
		fflush(stdin);
		SetColor(4, 15);
		scanf("%c", &choice);
		SetColor(0, 15);
		choice = toupper(choice);
	}
	title[i].user_ans = choice - 'A';//记录用户的选择
	if (title[i].user_ans == title[i].correct_ans)//如果答对了
	{
		printf("恭喜你,答对了!\n");
		system("pause");
		return 1;
	}
	printf("答错了,正确答案为:%c!\n", title[i].correct_ans + 'A');//如果答错了
	system("pause");
	return 0;
}

/**********用户开始回答选择题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 即数组上次测试答题后的长度*/
/*返回值:本次答题回答正确的题目数量*/
int Start_Choice(Title title[], int n)
{
	while (1)
	{
		int choice;
		int i;
		int count = 0;//记录回答正确的数量
		printf("1:简单\n");
		printf("2:中等\n");
		printf("3:困难\n");
		printf("请选择难度:");
		SetColor(4, 15);
		scanf("%d", &choice);
		SetColor(0, 15);
		if (choice != 1 && choice != 2 && choice != 3)
			continue;
		Sleep(500);
		printf("请输入题数:");
		SetColor(4, 15);
		scanf("%d", &TITLE_NUM);
		SetColor(0, 15);
		clock_t start, end;//计时
		start = clock();
		if (choice == 1)
		{
			for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
			{
				Creat_Choice_Title1(title, n + i);//生成题目
				count += Get_User_Choice(title, n, i);//回答题目
			}
			end = clock();
			printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
			system("pause");
			return count;//返回回答正确的数量
		}
		else if (choice == 2)
		{
			for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
			{
				Creat_Choice_Title2(title, n + i);//生成题目
				count += Get_User_Choice(title, n, i);//回答题目
			}
			end = clock();
			printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
			system("pause");
			return count;//返回回答正确的数量
		}
		else if (choice == 3)
		{
			for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
			{
				Creat_Choice_Title3(title, n + i);//生成题目
				count += Get_User_Choice(title, n, i);//回答题目
			}
			end = clock();
			printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
			system("pause");
			return count;//返回回答正确的数量
		}
		else
			continue;
	}
}

/**********生成简单难度填空题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 已答题的数量,即数组当前长度*/
void Creat_Fill_Title1(Title title[], int n)
{
	title[n].style = 1;//题目类型为填空题
	do
	{
		title[n].opt_num1 = (rand() % MAX_NUM1) + 1;//随机生成操作数1
		title[n].opt_num2 = (rand() % MAX_NUM1) + 1;//随机生成操作数2
		title[n].opt_ch = rand() % 2;//随机生成操作符
		switch (title[n].opt_ch)//根据操作符 生成正确答案等
		{
		case 0://加法的情况
			if (title[n].opt_num1 + title[n].opt_num2 <= MAX_NUM1)//确保结果在10以内
			{
				title[n].correct_ans = title[n].opt_num1 + title[n].opt_num2;//得到正确答案
				return;
			}
			break;
		case 1://减法的情况
			if (title[n].opt_num1 - title[n].opt_num2 > 0)//确保答案大于0
			{
				title[n].correct_ans = title[n].opt_num1 - title[n].opt_num2;//获取正确答案
				return;
			}
			break;
		}
	} while (1);
}

/**********生成中等难度填空题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 已答题的数量,即数组当前长度*/
void Creat_Fill_Title2(Title title[], int n)
{
	title[n].style = 1;//题目类型为填空题
	do
	{
		title[n].opt_num1 = (rand() % MAX_NUM2) + 1;//随机生成操作数1
		title[n].opt_num2 = (rand() % MAX_NUM2) + 1;//随机生成操作数2
		title[n].opt_ch = rand() % 2;//随机生成操作符
		switch (title[n].opt_ch)//根据操作符 生成正确答案等
		{
		case 0://加法的情况
			if (title[n].opt_num1 + title[n].opt_num2 <= MAX_NUM2)//确保结果在20以内
			{
				title[n].correct_ans = title[n].opt_num1 + title[n].opt_num2;//得到正确答案
				return;
			}
			break;
		case 1://减法的情况
			if (title[n].opt_num1 - title[n].opt_num2 > 0)//确保答案大于0
			{
				title[n].correct_ans = title[n].opt_num1 - title[n].opt_num2;//获取正确答案
				return;
			}
			break;
		}
	} while (1);
}

/**********生成困难难度填空题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 已答题的数量,即数组当前长度*/
void Creat_Fill_Title3(Title title[], int n)
{
	title[n].style = 1;//题目类型为填空题
	do
	{
		title[n].opt_num1 = (rand() % MAX_NUM) + 1;//随机生成操作数1
		title[n].opt_num2 = (rand() % MAX_NUM) + 1;//随机生成操作数2
		title[n].opt_ch = rand() % 2;//随机生成操作符
		switch (title[n].opt_ch)//根据操作符 生成正确答案等
		{
		case 0://加法的情况
			if (title[n].opt_num1 + title[n].opt_num2 <= MAX_NUM)//确保结果在100以内
			{
				title[n].correct_ans = title[n].opt_num1 + title[n].opt_num2;//得到正确答案
				return;
			}
			break;
		case 1://减法的情况
			if (title[n].opt_num1 - title[n].opt_num2 > 0)//确保答案大于0
			{
				title[n].correct_ans = title[n].opt_num1 - title[n].opt_num2;//获取正确答案
				return;
			}
			break;
		}
	} while (1);
}

/**********填空题获取用户的答案*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 即数组上次测试答题后的长度*/
/*参数3 int i: 当前测试的第i+1题*/
/*返回值:0代表该题答错  1代表回答正确*/
int Get_User_Fill(Title title[], int n, int i)
{
	system("cls");//清屏
	printf("第%d题: ", n + i + 1);//输出题号
	printf("%d ", title[n + i].opt_num1);//输出操作数1
	switch (title[n + i].opt_ch)//输出符号
	{
	case 0:printf("+ "); break;
	case 1:printf("- "); break;
	}
	printf("%d = \n", title[n + i].opt_num2);//输出操作数2
	printf("请输入答案:");
	while (getchar() != '\n');//清空缓冲区
	SetColor(4, 15);
	scanf("%d", &title[n + i].user_ans);
	SetColor(0, 15);
	if (title[n + i].user_ans == title[n + i].correct_ans)//如果答对了
	{
		printf("恭喜你,答对了!\n");
		system("pause");
		return 1;
	}
	printf("答错了,正确答案为:%d!\n", title[n + i].correct_ans);//如果答错了
	system("pause");
	return 0;
}

/****错题重答中填空题获取用户的答案****/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int i: 当前测试的第i+1题*/
/*返回值:0代表该题答错  1代表回答正确*/
int Get_User_Fill2(Title title[], int n, int i)
{
	system("cls");//清屏
	printf("第%d题: ", i + 1);//输出题号
	printf("%d ", title[i].opt_num1);//输出操作数1
	switch (title[i].opt_ch)//输出符号
	{
	case 0:printf("+ "); break;
	case 1:printf("- "); break;
	}
	printf("%d = \n", title[i].opt_num2);//输出操作数2
	printf("请输入答案:");
	while (getchar() != '\n');//清空缓冲区
	SetColor(4, 15);
	scanf("%d", &title[i].user_ans);
	SetColor(0, 15);
	if (title[i].user_ans == title[i].correct_ans)//如果答对了
	{
		printf("恭喜你,答对了!\n");
		system("pause");
		return 1;
	}
	printf("答错了,正确答案为:%d!\n", title[i].correct_ans);//如果答错了
	system("pause");
	return 0;
}

/**********用户开始回答填空题*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n: 即数组上次测试答题后的长度*/
/*返回值:本次答题回答正确的题目数量*/
int Start_Fill(Title title[], int n)
{
	while (1)
	{
		int i;
		int choice;
		int count = 0;//记录回答正确的数量
		printf("1:简单\n");
		printf("2:中等\n");
		printf("3:困难\n");
		printf("请选择难度:");
		SetColor(4, 15);
		scanf("%d", &choice);
		SetColor(0, 15);
		if (choice != 1 && choice != 2 && choice != 3)
			continue;
		Sleep(500);
		printf("请输入题数:");
		SetColor(4, 15);
		scanf("%d", &TITLE_NUM);
		SetColor(0, 15);
		clock_t start, end;
		start = clock();
		if (choice == 1)
		{
			for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
			{
				Creat_Fill_Title1(title, n + i);//生成题目
				count += Get_User_Fill(title, n, i);//回答题目
			}
			end = clock();
			printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
			system("pause");
			return count;//返回回答正确的数量
		}
		else if (choice == 2)
		{
			for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
			{
				Creat_Fill_Title2(title, n + i);//生成题目
				count += Get_User_Fill(title, n, i);//回答题目
			}
			end = clock();
			printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
			system("pause");
			return count;//返回回答正确的数量
		}
		else if (choice == 3)
		{
			for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
			{
				Creat_Fill_Title3(title, n + i);//生成题目
				count += Get_User_Fill(title, n, i);//回答题目
			}
			end = clock();
			printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
			system("pause");
			return count;//返回回答正确的数量
		}
		else
			continue;
	}
}

/**********选择答题方式的菜单*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int*n : 即数组上次测试答题后的长度*/
/*参数3 int*all : 即数组上次测试答题后回答正确的长度*/
/*参数4 int*last : 本次答题回答正确的题量*/
void Start_Menu(Title title[], int* n, int* all, int* last)
{
	int choice;
	system("cls");
	printf("1:选择题\n");
	printf("2:填空题\n");
	printf("0:返回\n");
	printf("请输入选项:");
	while (1)
	{
		SetColor(4, 15);
		if (scanf("%d", &choice) == 1)
		{
			SetColor(0, 15);
			if (choice >= 0 && choice <= 2)
			{
				break;
			}
			else
			{
				while (getchar() != '\n');
				printf("请重新输入选项:");
			}
		}
		else
		{
			SetColor(0, 15);
			while (getchar() != '\n');
			printf("请重新输入选项:");
		}
	}
	switch (choice)//根据选项 进入选择题或填空题
	{
	case 1: *last = Start_Choice(title, *n); *all += *last; *n += TITLE_NUM;  break;
	case 2: *last = Start_Fill(title, *n); *all += *last; *n += TITLE_NUM; break;
	}
}

/**********选择答题方式的菜单*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n : 即数组上次测试答题后的长度*/
/*参数3 int all : 即数组上次测试答题后回答正确的长度*/
/*参数4 int last : 本次答题回答正确的题量*/
void Get_State(Title title[], int n, int all, int last)
{
	int i;
	int count = 0;//记录错题数量
	system("cls");
	printf("当前总共做:%4d题\n", n);
	printf("当前共做对:%4d题\n", all);
	printf("上次共做对:%4d题\n", last);
	for (i = 0; i < n; i++)
	{
		if (title[i].user_ans != title[i].correct_ans)
			count++;
	}
	printf("总错题剩余:%4d题\n", count);
	system("pause");
}

/**********选择答题方式的菜单*********/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n : 即数组上次测试答题后的长度*/
void Test_Again(Title title[], int n)
{
	int i, flag = 0;
	system("cls");
	if (n == 0)
	{
		printf("当前没有错题\n");
		system("pause");
		return;
	}
	for (i = 0; i < n; i++)//遍历所有的错题
	{
		if (title[i].correct_ans != title[i].user_ans)//如果该题是错题
		{
			flag = 1;
			if (title[i].style == 0)//选择题
			{
				Get_User_Choice2(title, n, i);
			}
			else//填空题
			{
				Get_User_Fill2(title, n - TITLE_NUM, i);
			}
		}
	}
	if (!flag)
	{
		printf("当前没有错题\n");
	}
	system("pause");
}

/******************打印选择题试卷********************/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n : 即数组上次测试答题后的长度*/
/*参数3 FILE*fp1 : 题目所在文件*/
/*参数4 FILE*fp2 : 答案所在文件*/
void Write2Paper_Choice(Title title[], int n, FILE* fp1, FILE* fp2)
{
	int i;
	char ch;
	for (i = 0; i < n; i++)
	{
		if (title[i].opt_ch == 0) ch = '+';
		else ch = '-';
		fprintf(fp1, "第%d题: %d %c %d = \n", i + 1, title[i].opt_num1, ch, title[i].opt_num2);
		fprintf(fp2, "第%d题: %d %c %d = %c\n", i + 1, title[i].opt_num1, ch, title[i].opt_num2, title[i].correct_ans + 'A');
		fprintf(fp1, "A:%d  B:%d  C:%d  D:%d\n\n", title[i].choice[0], title[i].choice[1], title[i].choice[2], title[i].choice[3]);
		fprintf(fp2, "A:%d  B:%d  C:%d  D:%d\n\n", title[i].choice[0], title[i].choice[1], title[i].choice[2], title[i].choice[3]);
	}
}

/******************打印填空题试卷********************/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int n : 即数组上次测试答题后的长度*/
/*参数3 FILE*fp1 : 题目所在文件*/
/*参数4 FILE*fp4 : 答案所在文件*/
void Write2Paper_Fill(Title title[], int n, FILE* fp1, FILE* fp2)
{
	int i;
	char ch;
	for (i = 0; i < n; i++)
	{
		if (title[i].opt_ch == 0) ch = '+';
		else ch = '-';
		fprintf(fp1, "第%d题: %d %c %d = \n", i + 1, title[i].opt_num1, ch, title[i].opt_num2);
		fprintf(fp2, "第%d题: %d %c %d = %d\n", i + 1, title[i].opt_num1, ch, title[i].opt_num2, title[i].correct_ans);
	}
}

/*******系统使用说明********/
void Introduction()
{
	system("cls");
	printf("欢迎使用小学100以内整数加减运算系统帮助功能!\n");
	printf("以下是系统的主要功能和操作说明:\n");
	SetColor(4, 15);
	printf("1. * * 登录与答题 * *\n");
	SetColor(0, 15);
	printf("-输入‘1’选择“登录答题”选项,开始答题。\n");
	printf("-用户可以输入‘1’选择选择题或输入‘2’选择填空题形式进行答题,也可以输入‘0’返回。\n");
	printf("-用户可以输入‘1’选择简单模式,或输入‘2’选择普通模式,或输入‘3’选择困难模式。\n");
	printf("-用户可以自主输入生成的题数\n");
	printf("-系统将随机生成加减运算题目。\n");
	printf("-每道题目答题后,系统将即时反馈是否正确,并计入总分,同时反馈总用时。\n");
	SetColor(4, 15);
	printf("2. * * 查阅答题情况 * *\n");
	SetColor(0, 15);
	printf("-输入‘2’选择“查阅答题”选项,查看答题记录和得分。比如:\n");
	printf("当前总共做 : 30题\n");
	printf("当前共做对 : 25题\n");
	printf("上次共做对 : 8题\n");
	printf("总错题剩余 : 3题\n");
	printf("-注意:在答题期间不能查阅答题情况。\n");
	SetColor(4, 15);
	printf("3. * * 错题再答 * *\n");
	SetColor(0, 15);
	printf("-输入‘3’选择“错题再答”选项,重新回答之前答错的题目。\n");
	printf("-如果错题答对了,将从错题集中删除。\n");
	SetColor(4, 15);
	printf("4. * * 生成题目到文件 * *\n");
	SetColor(0, 15);
	printf("-输入‘4’选择“打印试卷”选项,将题目和答案保存到文本文件中。\n");
	printf("-用户可以自主决定文件保存的地址。\n");
	printf("-这可以用于线下测试或分享给他人。\n");
	SetColor(4, 15);
	printf("5. * * 系统帮助 * *\n");
	SetColor(0, 15);
	printf("-输入‘5’选择系统帮助选项,打开帮助菜单\n");
	printf("-输入‘1’打开系统使用说明,或输入‘2’进行模拟答题\n");
	SetColor(4, 15);
	printf("6. * * 退出系统 * *\n");
	SetColor(0, 15);
	printf("-输入‘0’选择“退出系统”选项,结束程序运行。\n");
	printf("注意事项:\n");
	printf("-请确保在答题期间保持专注,不要查阅答题情况。\n");
	printf("-如果遇到任何问题或错误,请随时查阅相关文档。\n");
	printf("希望以上帮助信息能够助您更好地使用本系统。如果您有任何疑问或建议,请随时与我们联系。\n");
	system("pause");
}

/********生成选择题测试*********/
void Start_Choice_Practise()
{
	char choice;//获取 用户选择
	system("cls");//清屏
	clock_t start, end;
	start = clock();
	printf("第1题: 21 + 55 =\n");
	printf("A:6    B:76    C:91    D:49\n");
	printf("请输入选择(可无视大小写):\n");
	while (getchar() != '\n');//清空缓冲区
	while (1)
	{
		SetColor(4, 15);
		if (scanf("%c", &choice) == 1)
		{
			SetColor(0, 15);
			choice = toupper(choice);
			if (choice >= 'A' && choice <= 'Z')
			{
				break;
			}
			else
			{
				while (getchar() != '\n');
				printf("请重新输入选项:");
			}
		}
		else
		{
			SetColor(0, 15);
			while (getchar() != '\n');
			printf("请重新输入选项:");
		}
	}
	if (choice == 'B')
	{
		printf("恭喜你,答对了!\n");
		end = clock();
		printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
		system("pause");
	}
	else
	{
		printf("答错了,正确答案为:B\n");//如果答错了
		system("pause");
	}
}

/*******生成填空题测试********/
void Start_Fill_Practise()
{
	int choice;//获取 用户选择
	system("cls");//清屏
	clock_t start, end;
	start = clock();
	printf("第1题: 21 + 55 =\n");
	printf("请输入答案:\n");
	while (getchar() != '\n');//清空缓冲区
	while (1)
	{
		SetColor(4, 15);
		if (scanf("%d", &choice) == 1)
		{
			SetColor(0, 15);
			if (choice >= 1 && choice <= 99)
			{
				break;
			}
			else
			{
				while (getchar() != '\n');
				printf("请重新输入选项:");
			}
		}
		else
		{
			SetColor(0, 15);
			while (getchar() != '\n');
			printf("请重新输入选项:");
		}
	}
	if (choice == 76)
	{
		printf("恭喜你,答对了!\n");
		end = clock();
		printf("总用时:%.2f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
		system("pause");
	}
	else
	{
		printf("答错了,正确答案为:76\n");//如果答错了
		system("pause");
	}
}

/*******生成测试********/
void Practise_Test()
{
	int choice;//系统帮助菜单中用户选择
	system("cls");
	printf("1:选择题\n");
	printf("2:填空题\n");
	printf("请输入选项:");
	while (1)
	{
		SetColor(4, 15);
		if (scanf("%d", &choice) == 1)
		{
			SetColor(0, 15);
			if (choice >= 1 && choice <= 2)
			{
				break;
			}
			else
			{
				while (getchar() != '\n');
				printf("请重新输入选项:");
			}
		}
		else
		{
			SetColor(0, 15);
			while (getchar() != '\n');
			printf("请重新输入选项:");
		}
	}
	switch (choice)//根据选项 进入选择题或填空题
	{
	case 1: Start_Choice_Practise();  break;
	case 2: Start_Fill_Practise();  break;
	}
}

/*******选择系统帮助的菜单******/
void Help_Menu()
{
	int choice;//系统帮助菜单中用户选择
	system("cls");
	printf("1:系统使用说明\n");
	printf("2:模拟答题\n");
	printf("请输入选项:");
	while (1)
	{
		SetColor(4, 15);
		if (scanf("%d", &choice) == 1)
		{
			SetColor(0, 15);
			if (choice >= 1 && choice <= 2)
			{
				break;
			}
			else
			{
				while (getchar() != '\n');
				printf("请重新输入选项:");
			}
		}
		else
		{
			SetColor(0, 15);
			while (getchar() != '\n');
			printf("请重新输入选项:");
		}
	}
	switch (choice)
	{
	case 1: Introduction(); break;
	case 2: Practise_Test(); break;
	}
}

/********生成试卷菜单*********/
void Print_Paper_Menu()
{
	system("cls");//清空屏幕输出
	printf("请选择试卷文件所在地址:\n");
	char address[100], address_paper[100], address_ans[100];// 定义字符数组用于存储地址
	SetColor(4, 15);
	scanf("%s", address);
	SetColor(0, 15);
	address[strcspn(address, "\n")] = 0;// 移除地址字符串末尾的换行符
	// 根据用户输入的地址拼接试卷和答案文件的路径
	snprintf(address_paper, sizeof(address_paper), "%s\\paper.txt", address);
	snprintf(address_ans, sizeof(address_ans), "%s\\ans.txt", address);
	int choice;//生成试卷菜单中用户选择
	int i, j;//用于迭代
	printf("请选择试卷文件中的题数:");
	SetColor(4, 15);
	scanf("%d", &TITLE_NUM);
	SetColor(0, 15);
	FILE* fp1, * fp2;// 定义文件指针,用于写入试卷和答案
	fp1 = fopen(address_paper, "w+");//保存试卷
	fp2 = fopen(address_ans, "w+");//保存答案
	Title title[1024];
	printf("1:生成选择题\n");
	printf("2:生成填空题\n");
	printf("3:混合生成\n");
	printf("0:返回\n");
	printf("请输入选项:");
	SetColor(4, 15);
	scanf("%d", &choice);//获取选择
	SetColor(0, 15);
	while (choice < 0 || choice>3)//判断输入的选择是否在0--4之间  不是的话重新输入
	{
		printf("请重新输入选项:");
		SetColor(4, 15);
		scanf("%d", &choice);
		SetColor(0, 15);
	}
	switch (choice)
	{
	case 1:
		for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
		{
			Creat_Choice_Title3(title, i);//生成题目
		}
		Write2Paper_Choice(title, TITLE_NUM, fp1, fp2);
		break;
	case 2:
		for (i = 0; i < TITLE_NUM; i++)//循环生成题目 回答题目
		{
			Creat_Fill_Title3(title, i);//生成题目
		}
		Write2Paper_Fill(title, TITLE_NUM, fp1, fp2);
		break;
	case 3:
		for (i = 0; i < TITLE_NUM / 2; i++)//循环生成题目 回答题目
		{
			Creat_Choice_Title3(title, i);//生成题目
		}
		Write2Paper_Choice(title, TITLE_NUM / 2, fp1, fp2);
		j = i;
		for (; i < TITLE_NUM; i++)//循环生成题目 回答题目
		{
			Creat_Fill_Title3(title, i);//生成题目
		}
		Write2Paper_Fill(title + j, TITLE_NUM - TITLE_NUM / 2, fp1, fp2);
		break;
	}
	if (choice != 0)
	{
		printf("试卷及答案打印成功!请打开文件查看!\n");
		system("pause");
	}
	// 刷新文件缓冲区,确保所有数据都写入文件
	fflush(fp1);
	fflush(fp2);
	fclose(fp1);
	fclose(fp2);
}

/*****将所有数据写入到文件*****/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int title_count:答题总数 */
/*参数3 int correct_count_all:正确答题总数 */
/*参数4 int correct_count_last:上一次正确答题数 */
void Save_File(Title title[], int title_count, int correct_count_all, int correct_count_last)
{
	FILE* fp = fopen(ID, "wb+ ");
	if (fp == NULL) return;

	fprintf(fp, "%d %d %d\n", title_count, correct_count_all, correct_count_last);
	fwrite(title, sizeof(Title), title_count, fp);
	fclose(fp);
}

/*****将所有数据从文件读取*****/
/*参数1 Title title[]:已经答过的题目数组,记录新生成的题目*/
/*参数2 int title_count:答题总数 */
/*参数3 int correct_count_all:正确答题总数 */
/*参数4 int correct_count_last:上一次正确答题数 */
void Load_File(Title title[], int* title_count, int* correct_count_all, int* correct_count_last)
{
	FILE* fp = fopen(ID, "rb+");
	if (fp == NULL) return;

	if (fscanf(fp, "%d %d %d\n", title_count, correct_count_all, correct_count_last) != 3)
	{
		*title_count = 0;
		*correct_count_all = 0;
		*correct_count_last = 0;
		fclose(fp);
	}
	fread(title, sizeof(Title), *title_count, fp);
	fclose(fp);
}

/*打印菜单*/
void menu()
{
	printf("1.注册\n");
	printf("2.登录\n");
	printf("3.找回密码\n");
	printf("请选择:");
}

/*获取光标x坐标*/
/*返回值:返回光标的x坐标*/
int posx()
{
	// 定义一个CONSOLE_SCREEN_BUFFER_INFO结构体变量ptr,用于存储控制台屏幕缓冲区的信息
	CONSOLE_SCREEN_BUFFER_INFO ptr;
	// 调用GetConsoleScreenBufferInfo函数,获取标准输出句柄对应的控制台屏幕缓冲区的信息,并存储到ptr中
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
	return (ptr.dwCursorPosition.X);// 返回ptr中存储的光标位置的x坐标
}

/*获取光标y坐标*/
/*返回值:返回光标的y坐标*/
int posy()
{
	CONSOLE_SCREEN_BUFFER_INFO ptr;
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
	return (ptr.dwCursorPosition.Y);// 返回ptr中存储的光标位置的y坐标
}

/********定位光标********/
void gotoxy(int x, int y)
{
	//更新光标位置
	COORD pos;// 定义一个COORD结构体变量pos,用于存储新的光标位置
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//GetStdHandle是一个Windows API函数。
	// 设置pos结构体的X和Y成员变量,即光标的横纵坐标
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(hOutput, pos);// 调用SetConsoleCursorPosition函数,将光标移动到指定的位置(pos)
}

/*****输入密码*****/
/**参数1:用户密码**/
void Getpwd(char* pwd)
{
	SetColor(4, 15);
	int i = 0;// 记录密码长度
	int x, y;// 记录光标位置
	while (1)
	{
		pwd[i] = _getch();//获取单个密码(字符)
		if (pwd[i] == VK_BACK && i >= 0)//如果输入的是回退键,VK_BACK是键盘的键值,ASCII码值为8
		{
			if (i > 0)//i>0说明有输入东西了,则回退前一格
			{
				i--;
				x = posx() - 1;//定位x并回退一格
				y = posy();//定位y
				gotoxy(x, y);//定位光标
				printf(" ");//将*用空格掩盖
				x = posx() - 1;//再次回退,下次输入时光标才会显示在正确的位置
				y = posy();//定位y
				gotoxy(x, y);
				continue;//然后跳过此次循环
			}
			else if (i == 0)//i==0说明未输入,直接跳过此次循环
			{
				continue;
			}
		}
		if (i >= 0 && pwd[i] != VK_RETURN && pwd[i] != VK_BACK)//存在输入
		{
			x = posx();
			y = posy();
			gotoxy(x, y);
			printf("*");
		}
		if (i == 0 && pwd[i] == VK_RETURN)//如果未输入直接回车,直接跳过此次循环,避免程序把回车当密码
		{
			continue;
		}
		if (pwd[i] == VK_RETURN || i == MAX_PWD - 2)//输入回车了或者到上限
		{
			i++;
			pwd[i] = '\0';//结尾放'\0'
			break;
		}
		i++;
	}
	SetColor(0, 15);
}

/***注册系统***/
void Register()
{
	Users a, b;//创建用户结构体临时变量,a是用来接收用户输入的,b是用来从文件中读取的,来和a作比较
	char tmp[20] = "";//用着下面判断的
	printf("欢迎来到注册界面!\n");
	FILE* pf = fopen("users.dat", "rb+");//"rb+"是以二进制只读方式打开文件的意思
	if (!pf)//如果打开失败
	{
		FILE* pf = fopen("users.dat", "wb+");
		Sleep(800);
		printf("请输入您的账号>>");
		SetColor(4, 15);
		scanf("%s", a.id);
		SetColor(0, 15);
		printf("请输入您的姓名>>");
		SetColor(4, 15);
		scanf("%s", a.name);
		SetColor(0, 15);
		printf("请输入您的性别>>");
		SetColor(4, 15);
		scanf("%s", a.sex);
		SetColor(0, 15);
		printf("请输入您的电话号码>>");
		SetColor(4, 15);
		scanf("%s", a.phone);
		SetColor(0, 15);
		printf("请输入您的密码>>");
		Getpwd(a.pwd);//输入密码的函数,有掩盖功能
		printf("\n请再次确认您的密码>>");
		Getpwd(tmp);
		do
		{
			if (!strcmp(a.pwd, tmp))//两次密码相等
			{
				pf = fopen("users.dat", "ab");
				fwrite(&a, sizeof(Users), 1, pf);
				printf("\n账号注册成功,请登录!\n");
				Sleep(500);
				fclose(pf);
				pf = NULL;
				return;
			}
			else
			{
				printf("\n两次密码不匹配!请重新输入>>");
				Getpwd(a.pwd);
				printf("\n请再次确认>>");
				Getpwd(tmp);
			}
		} while (1);
	}
	else
	{
		fread(&b, sizeof(Users), 1, pf);//将数据读入b中
		Sleep(800);
		printf("请输入您的账号>>");
		SetColor(4, 15);
		scanf("%s", a.id);
		SetColor(0, 15);

		while (1)
		{
			if (strcmp(a.id, b.id))//两个用户名不相等
			{
				if (!feof(pf))//未读到文件末尾
				{
					fread(&b, sizeof(Users), 1, pf);//继续读取文件中的用户到b中
				}
				else//读到文件末尾了,确认没有重复的id
				{
					break;
				}
			}
			else//有两个用户名相等
			{
				printf("此用户名已存在在,请重试!\n");
				Sleep(1000);
				fclose(pf);//关闭文件
				pf = NULL;//pf置空,避免野指针
				return;
			}
		}
		printf("请输入您的姓名>>");
		SetColor(4, 15);
		scanf("%s", a.name);
		SetColor(0, 15);
		printf("请输入您的性别>>");
		SetColor(4, 15);
		scanf("%s", a.sex);
		SetColor(0, 15);
		printf("请输入您的电话号码>>");
		SetColor(4, 15);
		scanf("%s", a.phone);
		SetColor(0, 15);
		printf("请输入您的密码>>");
		Getpwd(a.pwd);//输入密码的函数,有掩盖功能
		printf("\n请再次确认您的密码>>");
		Getpwd(tmp);
		do
		{
			if (!strcmp(a.pwd, tmp))//两次密码相等
			{
				pf = fopen("users.dat", "ab");
				fwrite(&a, sizeof(Users), 1, pf);
				printf("\n账号注册成功,请登录!\n");
				Sleep(500);
				fclose(pf);
				pf = NULL;
				return;
			}
			else
			{
				printf("\n两次密码不匹配!请重新输入>>");
				Getpwd(a.pwd);
				printf("\n请再次确认>>");
				Getpwd(tmp);
			}
		} while (1);
	}
}

/**登录系统**/
/*返回值:1代表登录成功 0代表登录失败*/
int Login()
{
	Users a, b;//a是用来给用户输入,从文件中读取到b中,用b和a匹配比较
	FILE* pf = fopen("users.dat", "rb");//以只读的方式打开文件
	if (!pf)//如果读取失败
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	printf("欢迎来到登录界面!\n");
	Sleep(900);
	fread(&b, sizeof(Users), 1, pf);//先从文件中读取一个用户
	printf("请输入账号>>");
	SetColor(4, 15);
	scanf("%s", a.id);
	SetColor(0, 15);
	while (1)
	{
		if (!strcmp(a.id, b.id))//在文件中找到了与用户输入相同的id
		{
			break;
		}
		else
		{
			if (!feof(pf))//未读到文件末尾,继续读取文件中的id到b中
			{
				fread(&b, sizeof(Users), 1, pf);//继续从文件中读取用户信息进b,直到在文件中找到一个和a的信息相同的
			}
			else//读到文件末尾,没有找到与用户输入相同的账号
			{
				printf("此账号不存在!请重试!\n");
				Sleep(500);
				if (Login() == 1)
				{
					return 1;
				}
			}
		}
	}
	strcpy(ID, b.id);
	strcat(ID, ".dat");
	do
	{
		printf("请输入密码>>");
		Getpwd(a.pwd);//获取密码
		if (!strcmp(a.pwd, b.pwd))//输入的密码与文件中的相同
		{
			printf("\n登录成功!欢迎使用!\n");
			Sleep(500);
			fclose(pf);
			pf = NULL;//置空,避免野指针
			return 1;
		}
		else
		{
			printf("\n密码输入错误,请重新输入\n");
		}
	} while (strcmp(a.pwd, b.pwd));
}

/*找回密码*/
void Reback()
{
	char tmp[20] = "";//密码匹配用的
	Users a, b;
	FILE* pf = fopen("users.dat", "rb+");//"rb+"是为了读和写以二进制打开文件
	if (!pf)//判断能否顺利打开
	{
		printf("%s", strerror(errno));
		return;
	}
	fread(&b, sizeof(Users), 1, pf);
	printf("请输入您的账号>>");
	Sleep(800);
	SetColor(4, 15);
	scanf("%s", a.id);
	SetColor(0, 15);
	while (1)//在文件中找到与用户输入相同的id
	{
		if (!strcmp(a.id, b.id))//如果读取到了相同的id(在文件中找到了和用户输入一样的)
		{
			break;
		}
		else
		{
			if (!feof(pf))//未读到文件尾,继续读
			{
				fread(&b, sizeof(Users), 1, pf);
			}
			else
			{
				printf("您输入的账号不存在!请重新输入!\n");
				Sleep(500);
				fclose(pf);
				pf = NULL;
				Reback();
				return;
			}
		}
	}
	//下面是信息匹配验证
	do//匹配姓名
	{
		printf("请输入您的姓名>>");
		SetColor(4, 15);
		scanf("%s", a.name);
		SetColor(0, 15);
		if (!strcmp(a.name, b.name))
		{
			break;
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	} while (strcmp(a.name, b.name));
	do//匹配性别
	{
		printf("请输入您的性别>>");
		SetColor(4, 15);
		scanf("%s", a.sex);
		SetColor(0, 15);
		if (!strcmp(a.sex, b.sex))
		{
			break;
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	} while (strcmp(a.sex, b.sex));

	do//匹配电话号码
	{
		printf("请输入您的电话号码>>");
		SetColor(4, 15);
		scanf("%s", a.phone);
		SetColor(0, 15);
		if (!strcmp(a.phone, b.phone))
		{
			break;
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	} while (strcmp(a.phone, b.phone));

	//更改密码
	printf("验证成功!请修改您的密码!\n");
	printf("请输入您的密码>>");
	Getpwd(a.pwd);
	printf("请再次确认您的密码>>");
	Getpwd(tmp);
	if (!pf)
	{
		printf("%s", strerror(errno));
		return;
	}
	//将原来的密码覆盖掉
	do
	{
		if (!strcmp(a.pwd, tmp))//两次密码相等
		{
			fseek(pf, -((int)(sizeof(Users) - MAX_ID)), SEEK_CUR);//将文件流调回到要修改的密码的位置
			fprintf(pf, "%s", a.pwd);//覆盖原来的密码
			printf("密码修改成功,请登录!\n");
			Sleep(500);
			fclose(pf);
			pf = NULL;
			return;
		}
		else
		{
			printf("两次密码不匹配!请重新输入>>");
			SetColor(4, 15);
			Getpwd(a.pwd);
			SetColor(0, 15);
			printf("请再次确认>>");
			SetColor(4, 15);
			Getpwd(tmp);
			SetColor(0, 15);
		}
	} while (1);
}

/*********界面登录*********/
/*返回值:1代表登录成功 0代表登录失败*/
int Sign_In()
{
	char choice;//用户选择
	do
	{
		menu();
		setbuf(stdin, NULL);
		SetColor(4, 15);
		choice = getchar();
		SetColor(0, 15);
		switch (choice)
		{
		case '1':
			Register();
			break;
		case '2':
			if (Login() == 1)
			{
				return 1;
			}
		case '3':
			Reback();
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	} while (choice != '0');
}



/*********主函数***********/
int main()
{
	system("color F0");//白底黑字
	if (!Sign_In()) return 0;//登录界面
	int choice;//主菜单用户选择
	Title title[1000];//题目数组
	int title_count = 0;//总题目长度
	int correct_count_all = 0;//总正确
	int correct_count_last = 0;//上一次正确
	Load_File(title, &title_count, &correct_count_all, &correct_count_last);
	srand(time(NULL));//初始化随机数种子
	do
	{
		choice = Main_Menu();
		switch (choice)
		{
		case 1:Start_Menu(title, &title_count, &correct_count_all, &correct_count_last); break;
		case 2:Get_State(title, title_count, correct_count_all, correct_count_last); break;
		case 3:Test_Again(title, title_count); break;
		case 4:Print_Paper_Menu(); break;
		case 5:Help_Menu(); break;
		}
	} while (choice != 0);
	Save_File(title, title_count, correct_count_all, correct_count_last);
	return 0;
}

5、PS

(1)额外增加了用户自定义题目数量的功能,可以将全局变量中TITLE_NUM改为宏定义从而固定题数为10

(2)在登录答题中增加了用户选择难度的功能,但打印试卷时默认困难难度(因为代码重复率太高,会显得臃肿,虽然已经很臃肿了)

(3)部分功能在用户输入时做了较严格的输入限制,部分功能未完缮(因为懒,而且没必要把用户当傻子),但欢迎大家自行完缮

(4)增加了打印试卷时用户自定义文件路径功能,请使用绝对路径,如:C:\Users\user\source\repos

(5)打印试卷时,也可以自定义题目数量,但如果选择混合输出并且题目数量为奇数时,比如选择题数为9,则只会生成4道选择与4道填空(虽然该功能修复较为简易,但还是懒得改,欢迎大家自行尝试)

(6)教学要求中要求图形界面(GUI),但未能完成,请大家自行探索

(7)本文中代码依旧存在一些bug,但不影响使用,欢迎大家指正

(8)本文使用Visual Studio 2022,控制台为Windows的cmd,请确保编译环境一致

6、参考资料

程序设计题:小学 100 以内正整数加减运算_请针对100以内正整数加法运算的计算器设计2条测试用例-CSDN博客

感谢作者提供的框架代码,虽然存在数组溢出的bug,但结构十分清晰精美

【C语言】简易登录注册系统(登录、注册、改密、文件操作)_c语言设计注册登录-CSDN博客​​​​​

感谢作者的登录系统,虽然有一些逻辑上的小bug,在修改之后十分实用(ps:使用本文代码中不需要如此文章中所说自行创建users.txt,已添加函数自行创建)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值