C语言实现可写入文件的账号密码登录系统,密码输入时掩盖,登录界面菜单选择,更改密码系统,课设必备。

账号登录系统在很多系统设计时都时必不可少的,今天这个登录系统功能较全,可以注册,登录,找回密码,修改密码,以及在输入密码时会有*将密码掩盖,还有完善地登录选择界面,废话不多,上代码!

目录

1.头文件&结构体&大概函数实现

 2.主函数&登录界面

3.注册系统

4.登录系统

5.找回密码(更改密码)

6.密码输入获取&密码掩盖

7.完整代码!!!!!


!!!!首先需要在和.c文件同个目录下新建一个文件储存数据,命名users.dat  如图!!

 

!!!!!!!!!!!!!!!!!!!! 

也可以命名别的,后缀dat是我自己随便取的,喜欢就行!但是记得在代码中将文件名改过来!

1.头文件&结构体&大概函数实现

//引头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>//用于反应意外情况的,下面会讲到
#include <windows.h>

//定义宏常量
#define MAX_ID 11//ID的最大长度
#define MAX_PWD 20//密码的最大长度
#define MAX_NAME 15//名字的最大长度
#define MAX_SEX 5//性别的最大长度
#define MAX_PHONE 12//电话的最大长度


//创建用户的结构体
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;

//声明函数

//打印菜单
void menu();

//用户注册
void Register();

//登录
void Login();

//找回密码
void Reback();



//定位光标
void gotoxy();

//获取x的位置
int posx();

//获取y的位置
int posy();

//密码输入(含掩盖功能)
void Getpwd(char* pwd);

这里定义了很多宏常量,可以看情况修改合适的数值,比较快捷方便,不用到代码里一个个改

 2.主函数&登录界面

void menu()
{
	printf("************************************n");
	printf("************************************n");
	printf("*****1.Register         2.Login*****n");
	printf("*****3.Reback           0.exit *****n");
	printf("************************************n");
	printf("************************************n");
}
 
int main()
{
	char input;
	do
	{
		menu();
		setbuf(stdin, NULL);//清空输入缓冲区,避免下面getchar读到上一次输入缓冲区剩下的东西
		input = getchar();
		switch (input)
		{
		case '1':
			Register();
			break;
		case '2':
			Login();
			break;
		case '3':
			Reback();
			break;
		case '0':
			printf("退出成功!n");
			break;
		default:
			printf("选择错误,请重新选择!n");
			break;
		}
	} while (input!='0');//如果输入了0,就exit退出咯
}

这里用到字符去当作switch的判断依据,是因为如果用整形去做判断依据的话,若向input中输入的东西不是整形而是字符或者字符串,switch在循环中就会陷入死循环,具体情况大家去试试就知道,这里不多讲解,用单个字符判断到现在是没试出什么bug。

3.注册系统

void Register()
{
	Users a, b;//创建用户结构体临时变量,a是用来接收用户输入的,b是用来从文件中读取的,来和a作比较

	char tmp[20]="";//用着下面判断的

	printf("欢迎来到注册界面!n");
	FILE* pf = fopen("users.dat", "rb");//"rb"是以二进制只读方式打开文件的意思
	fread(&b, sizeof(Users), 1, pf);//将数据读入b中
	if (!pf)//如果打开失败
	{
		printf("%s", strerror(errno));//errno可以理解是报的错误对应的编号,strerror可以把这个编号翻译出来,输出在屏幕上
		return;
	}
	Sleep(800);

	printf("请输入您的账号>>");
	scanf("%s", a.id);
	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("请输入您的姓名>>");
	scanf("%s", a.name);
	printf("请输入您的性别>>");
	scanf("%s", a.sex);
	printf("请输入您的电话号码>>");
	scanf("%s", a.phone);
	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);
}

 文件都是以二进制打开以二进制写入的,没别的原因,就是觉得帅!

4.登录系统

void Login()
{
	Users a, b;//同理,a是用来给用户输入的,b是用来是水的(从文件中读取到b中,用b和a匹配比较)

	FILE* pf = fopen("users.dat", "rb");//以只读的方式打开文件
	if (!pf)//如果读取失败
	{
		printf("%sn", strerror(errno));//上面有讲过~
		return;
	}
	printf("欢迎来到登录界面!n");
	Sleep(1000);

	fread(&b, sizeof(Users), 1, pf);//先从文件中读取一个用户试试水

	printf("请输入账号>>");
	scanf("%s", a.id);

	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);
				fclose(pf);
				pf = NULL;
				return;
			}
		}
	}
	do
	{
		printf("请输入密码>>");
		Getpwd(a.pwd);//获取密码,在下面会讲
		if (!strcmp(a.pwd, b.pwd))//输入的密码与文件中的相同
		{
			printf("n登录成功!欢迎使用!n");
			Sleep(500);
			fclose(pf);//用完当然要把文件关啦
			pf = NULL;//置空,避免野指针
			return;
		}
		else
		{
			printf("n密码输入错误,请重新输入n");
		}
	} while (strcmp(a.pwd, b.pwd));
}

大概思路就是用户先输入账号(id),接着b从文件中读取用户,直到找到与用户输入id相同的,然后就是输入密码判断密码对不对咯。   里面用到了一个库函数strcmp,判断字符串是不是一样的,如果一样则返回0,不一样则返回一个非0值。

5.找回密码(更改密码)

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);

	scanf("%s", a.id);

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

	//下面是信息匹配验证
	do//匹配姓名
	{
		printf("请输入您的姓名>>");
		scanf("%s", a.name);
		if (!strcmp(a.name, b.name))
		{
			break;
		}
		else
		{
			printf("输入错误,请重新输入!n");
		}
	} while (strcmp(a.name, b.name));

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

	//更改密码
	printf("验证成功!请修改您的密码!n");
	printf("请输入您的密码>>");
	Getpwd(a.id);
	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("两次密码不匹配!请重新输入>>");
			scanf("%s", a.pwd);
			printf("请再次确认>>");
			scanf("%s", tmp);
		}
	} while (1);
}

大概思路是输入账号->匹配信息->修改密码,修改密码时要将文件流调回要修改的密码的前面,这里用到了fseek,-((int)(sizeof(Users)-MAX_ID))是要调回的范围。

6.密码输入获取&密码掩盖

void gotoxy(int x, int y)
{
	//更新光标位置
	COORD pos;
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//GetStdHandle是一个Windows API函数。
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(hOutput, pos);
}

int posx()
{
	CONSOLE_SCREEN_BUFFER_INFO ptr;
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
	return (ptr.dwCursorPosition.X);
}

int posy()
{
	CONSOLE_SCREEN_BUFFER_INFO ptr;
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
	return (ptr.dwCursorPosition.Y);
}


void Getpwd(char* pwd)
{
	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] = '';//结尾放''
			break;
		}
		i++;
	}
}

那几个长长的都是windows控制台的API函数,这里不多讲解,有兴趣的大哥可以翻阅下面的链接~

GetStdHandle

SetConsoleCursorPosition

GetConsoleScreenBufferInfo

COORD

7.完整代码!!!!!

//引头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <windows.h>

//定义宏常量
#define MAX_ID 11
#define MAX_PWD 20
#define MAX_NAME 15
#define MAX_SEX 5
#define MAX_PHONE 12


//创建用户的结构体
typedef struct Users
{
	char id[MAX_ID];
	char pwd[MAX_PWD];
	char name[MAX_NAME];
	char sex[MAX_SEX];
	char phone[MAX_PHONE];
}Users;



//声明函数

//打印菜单
void menu();

//用户注册
void Register();

//登录
void Login();

//找回密码
void Reback();

//定位光标
void gotoxy();

//获取x的位置
int posx();

//获取y的位置
int posy();

//密码输入(含掩盖功能)
void Getpwd(char* pwd);

int main()//主函数
{
	char input;
	do
	{
		menu();
		setbuf(stdin, NULL);
		input = getchar();
		switch (input)
		{
		case '1':
			Register();
			break;
		case '2':
			Login();
			break;
		case '3':
			Reback();
			break;
		case '0':
			printf("退出成功!n");
			break;
		default:
			printf("选择错误,请重新选择!n");
			break;
		}
	} while (input!='0');
}

//打印菜单
void menu()
{
	printf("************************************n");
	printf("************************************n");
	printf("*****1.Register         2.Login*****n");
	printf("*****3.Reback           0.exit *****n");
	printf("************************************n");
	printf("************************************n");
}
 
//注册系统
void Register()
{
	Users a, b;//创建用户结构体临时变量,a是用来接收用户输入的,b是用来从文件中读取的,来和a作比较

	char tmp[20]="";//用着下面判断的

	printf("欢迎来到注册界面!n");
	FILE* pf = fopen("users.dat", "rb");//"rb"是以二进制只读方式打开文件的意思
	fread(&b, sizeof(Users), 1, pf);//将数据读入b中
	if (!pf)//如果打开失败
	{
		printf("%s", strerror(errno));//errno可以理解是报的错误对应的编号,strerror可以把这个编号翻译出来,输出在屏幕上
		return;
	}
	Sleep(800);

	printf("请输入您的账号>>");
	scanf("%s", a.id);
	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("请输入您的姓名>>");
	scanf("%s", a.name);
	printf("请输入您的性别>>");
	scanf("%s", a.sex);
	printf("请输入您的电话号码>>");
	scanf("%s", a.phone);
	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);
}

//登录系统
void Login()
{
	Users a, b;//同理,a是用来给用户输入的,b是用来是水的(从文件中读取到b中,用b和a匹配比较)

	FILE* pf = fopen("users.dat", "rb");//以只读的方式打开文件
	if (!pf)//如果读取失败
	{
		printf("%sn", strerror(errno));//上面有讲过~
		return;
	}
	printf("欢迎来到登录界面!n");
	Sleep(1000);

	fread(&b, sizeof(Users), 1, pf);//先从文件中读取一个用户试试水

	printf("请输入账号>>");
	scanf("%s", a.id);

	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);
				fclose(pf);
				pf = NULL;
				return;
			}
		}
	}
	do
	{
		printf("请输入密码>>");
		Getpwd(a.pwd);//获取密码,在下面会讲
		if (!strcmp(a.pwd, b.pwd))//输入的密码与文件中的相同
		{
			printf("n登录成功!欢迎使用!n");
			Sleep(500);
			fclose(pf);//用完当然要把文件关啦
			pf = NULL;//置空,避免野指针
			return;
		}
		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);

	scanf("%s", a.id);

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

	//下面是信息匹配验证
	do//匹配姓名
	{
		printf("请输入您的姓名>>");
		scanf("%s", a.name);
		if (!strcmp(a.name, b.name))
		{
			break;
		}
		else
		{
			printf("输入错误,请重新输入!n");
		}
	} while (strcmp(a.name, b.name));

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

	//更改密码
	printf("验证成功!请修改您的密码!n");
	printf("请输入您的密码>>");
	Getpwd(a.id);
	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("两次密码不匹配!请重新输入>>");
			scanf("%s", a.pwd);
			printf("请再次确认>>");
			scanf("%s", tmp);
		}
	} while (1);
}

//定位光标
void gotoxy(int x, int y)
{
	//更新光标位置
	COORD pos;
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//GetStdHandle是一个Windows API函数。
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(hOutput, pos);
}
//获取光标x坐标
int posx()
{
	CONSOLE_SCREEN_BUFFER_INFO ptr;
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
	return (ptr.dwCursorPosition.X);
}
//获取光标y坐标
int posy()
{
	CONSOLE_SCREEN_BUFFER_INFO ptr;
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
	return (ptr.dwCursorPosition.Y);
}

//输入密码
void Getpwd(char* pwd)
{
	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] = '';//结尾放''
			break;
		}
		i++;
	}
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>