【C语言】简易登录注册系统(登录、注册、改密、文件操作)

2023-10-29

概述:

        本登录注册系统通过使用C语言中的结构体、函数、文件操作以及指针等,设计与实现了一个小型用户登录注册系统的登录、注册、修改密码等基本功能。

        本系统全部功能基本运行良好、用户界面友好、操作简单、使用方便。但系统仍然有不完善之处。例如在隐藏用户输入密码时,如果用户要删除刚输入的密码,则不能将输入‘*’一并删除;修改密码时生成的验证码不应该直接显示,而是通用户留存的手机号将验证码发送至用户绑定的手机上;其实改密部分还可以再添加一个二次输入验证的功能。除此之外,还可以将程序中一些重复使用的代码段再行封装成函数,但考虑接口会变得更复杂,故并未操作。

用户信息采用结构体:

typedef struct The_users {
    char name[11];        
    char pwd[20];       
    char phone[11]; 
} Users;  

程序源码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>

typedef struct The_users { //typedef 可以定义结构体别名
	char name[11]; 		//账号
	char pwd[20]; 		//密码
	char phone[11];		//手机号
} Users;	//该结构体别名

void Options(int);	//菜单导航
void Registers();	//注册模块
void Login();		//登录模块
void Reback();		//密码找回模

int i = 0,flag = 1,menu = 0;
int main() {
	printf("\n\t\t\t++++++++++++++++++++++++\n\t\t\t+  欢迎使用XX登录系统  +\n\t\t\t++++++++++++++++++++++++\n");
//    system ("color 1F");			//程序面板颜色
	while (flag) {
		do {
			printf("\n		******************菜单栏*****************\n");
			printf("\t\t*\t      ①登录                    *\n");
			printf("\t\t*\t      ②没有账号?去注册        *\n");
			printf("\t\t*\t      ③忘记密码?去找回        *\n");
			printf("\t\t*\t      ④退出系统\t\t*\n");
			printf("		*****************************************\n");
			printf("\n");
			printf("\t\t请输入选项:[\t]\b\b\b");
			scanf("%d", &menu);
		} while (menu>5 || menu<1);
		Options(menu);
	}
}

/*
	通过Option函数
	接收代表功能对应的数字
	来调用其他功能函数
*/
void Options(int option) {
	switch (option) {
		case 1:
			Login();
			break;
		case 2:
			Registers();
			break;
		case 3:
			Reback();
			break;
		case 4:
			printf("\n\t\t谢谢使用(^V^)!\n");
			flag=0;		//flag置0,结束主函数循环(也可以不要此句)
			exit(0);
	}
}

/*
	注册账号模块
*/
void Registers() {
	system("cls");
	Users a,b;		//定义两个临时用结构体变量
	FILE *fp;
	char temp_pwd[20];
	int count = 0;
	printf("\n\t\t欢迎来到注册界面!\n");
	Sleep(500);
	fp = fopen("Users.txt","r");
	fread(&b, sizeof(Users), 1, fp); 	//读入一个用户信息,用于在下面查找注册的账号名是否存在
	printf("\n\t\t请设置账号:[ \t\t ]\b\b\b\b\b\b\b\b\b\b\b\b\b");
	scanf("%s",&a.name);
	while (1) {
		if (strcmp(a.name, b.name)) { 	//如果注册账号不存在
			if (!feof(fp)) {  						//如果未到文件尾
				fread(&b, sizeof(Users), 1, fp);	//继续读入下一个用户信息
			} 
			else {
				break;		//检查完毕,跳出
			}
		} 
		else {	//如果此账号已存在,退出
			printf("\t\t此用户名已存在!请重新注册!\n");
			Sleep(1000);
			fclose(fp);
			return;
		}
	}
	printf("\t\t请输入您的手机号:[ \t\t ]\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
	scanf("%s",&a.phone);
	int time = 2;
	do {
		printf("\t\t请设置密码:[ \t\t ]\b\b\b\b\b\b\b\b\b\b\b\b\b");
		i = 0;
		/*
		以下代码段用于将输入的明文替换为'*'
		起到保护作用,但仍存一点bug
		*/
		do {
			a.pwd[i]=getch();
			if(a.pwd[i]=='\r')
				break;
			if(a.pwd[i]=='\b') {	//如果退格(删除键)
				if(i==0) {			//依旧可以让输入的内容
					printf("\a");	//正确保存到密码中
					continue;
				}
				i=i-1;
				printf("\b");
			} 
			else {
				i=i+1;
				printf("*");		//但已经打印出的'*'是不能消除的,此程序bug
			}
		} while(a.pwd[i]!='\n' && i<20);	//当接收到回车符或者密码长度已够时完成密码接收
		a.pwd[i]='\0';
		printf("\n");
		printf("\t\t请确认密码:[ \t\t ]\b\b\b\b\b\b\b\b\b\b\b\b\b");
		/*
			再接收一次密码
			用于验证,防止用户设置密码出错
		*/
		i= 0;
		do {
			temp_pwd[i]=getch();
			if(temp_pwd[i]=='\r')
				break;
			if(temp_pwd[i]=='\b') {
				if(i==0) {
					printf("\a");
					continue;
				}
				i=i-1;
				printf("\b");
			} 
			else {
				i=i+1;
				printf("*");
			}
		} while(temp_pwd[i]!='\n' && i<20);
		temp_pwd[i]='\0';
		printf("\n");
		if(!strcmp(a.pwd,temp_pwd)) {	//如果两次输入的密码相同就可以注册了
			fp = fopen("Users.txt","a");	// a-向文件尾部追加写入
			fwrite(&a, sizeof(Users), 1, fp);
			printf("\t\t账号注册成功!\n");
			fclose(fp);
			return;
		} 
		else {
			if (time!=0) {
				printf("\n\t\t两次输入的密码不相同!\t请重新输入(剩余%d次机会)\n",time);
				time--;
				continue;	//如果本次密码匹配不相同跳出,进入下一次输入
			} 
			else {
				printf("\n\t\t多次输入错误,即将退出!");
				Sleep(1500);
				return ;
			}
		}
	} while(time >= 0);
}

/*
	登录模块
*/
void Login() {
	system("cls");
	Users a,b;	//定义结构体The_users别名
	FILE *fp;
	printf("\t\t\n欢迎来到登录界面!\n\n");
	printf("\t\t请输入账号:[ \t\t ]\b\b\b\b\b\b\b\b\b\b\b\b\b");
	scanf("%s",&a.name);
	printf("\t\t请输入密码:[ \t\t ]\b\b\b\b\b\b\b\b\b\b\b\b\b");
	i= 0;
	/*
		采用相同的方法掩盖明文密码
	*/
	do {
		a.pwd[i]=getch();
		if(a.pwd[i]=='\r')
			break;
		if(a.pwd[i]=='\b') {
			if(i==0) {
				printf("\a");
				continue;
			}
			i=i-1;
			printf("\b");
		} 
		else {
			i=i+1;
			printf("*");
		}
	} while(a.pwd[i]!='\n' && i<20);
	a.pwd[i]='\0';
	printf("\n");
	fp = fopen("Users.txt","r");
	while (1) {
		fread(&b, sizeof(Users), 1, fp); //读入一个结构体字符块 写入b
		if ((strcmp(a.name, b.name)==0)||(!feof(fp))) {      	//如果有此账号
			break;								//此账号已经注册 可以登录
		} 
		else {
			if (!feof(fp)) { //如果文件没有读完
				fread(&b, sizeof(Users), 1, fp);
			} 
			else {	//如果在变量用户表之后没有找到该账号,提示错误信息
				printf("\t\t账号或密码错误!\n");
				fclose(fp);
				Sleep(1000);
				system("cls");
				return ;
			}
		}
	}
	fclose(fp);
	if (strcmp(a.pwd, b.pwd)==0) {          /*如果密码匹配*/
		printf("\t\t登录成功!欢迎使用!\n");
		return;
	} 
	else {
		printf("\t\t账号或密码错误!……");
		Sleep(1000);
		system("cls");
		return;
	}
}

/*
	找回密码模块
*/
void Reback() {
	system("cls");
	Users a,b;	//两个用于零时写入的结构体变量
	FILE *fp;
	printf("\t\t欢迎来到找回密码界面!\n\n");
	int num = 0;	//用于记录改密用户在文件中的位置
	fp = fopen("Users.txt","r");
	fread(&b, sizeof(Users), 1, fp); //读入一个用户信息并写入b
	printf("\t\t请输入账号:[ \t\t ]\b\b\b\b\b\b\b\b\b\b\b\b\b");
	scanf("%s",&a.name);
	while (1) {
		if (strcmp(a.name, b.name)==0) {    //如果有此用户名
			break;						//就跳出完成查找
		} 
		else {
			if (!feof(fp)) { 			//如果文件没有读完切没有找到该用户
				fread(&b, sizeof(Users), 1, fp);	//继续查找用户
				num++;
			} 
			else {		//如果没有找到
				printf("\t\t此用户不存在!!!\n");
				fclose(fp);
				for(i=0; i<2; i++) {
					printf("\t\t%d秒后退出 ……",2-i);
					printf("\r");
					Sleep(1000);
				}
				system("cls");
				return;
			}
		}
	}
	//如果程序走到这 就可以开始修改密码了
	int ver_code = 0,user_ver_code = 0;
	//生成随机的验证码
	srand((unsigned)time(NULL));
	for (i = 0; i < 7; i++){
		ver_code += rand() % 1000;	
	}
	char phone_tail[5] ;	// 获取该用户的手机尾号
	strncpy(phone_tail,a.phone+7,4) ;
	printf("\t\t已向尾号为%s的手机号码发送验证码 %d ,请注意查收!\n\t\t请输入验证码:[\t\t]\b\b\b\b\b\b\b\b\b\b", phone_tail,ver_code);
	scanf("%d", &user_ver_code);
	int sign = 0;
	//给用户提供三次验证输入的机会
	for(i=0; i<3; i++) {
		if (ver_code != user_ver_code) {
			printf("\t\t验证码错误!请重新输入:[\t](剩%d次机会)\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",2-i);
			scanf("%d", &user_ver_code);
			continue;
		} 
		else if (i<=3) {	//如果验证次数内正确输入验证码
			sign =1;		//就可以退出循环
			break;			//向下一步改密
		} 
		else {
			for(i=0; i<2; i++) {
				printf("\t\t多次输入错误!!%d秒后退出 ……",2-i);
				printf("\r");
				Sleep(1000);
			}
		}
	}
	if(sign == 1) {		//如果验证手机正确开始改密
		FILE*fp;
		int N = 0;	//用于记录当前用户总数
		if((fp=fopen("Users.txt","r"))==NULL) {		// 判断文件是否能打开
			printf ("\t\tCannot open file\n");
			exit(0);
		} 
		else {
			while (feof(fp)==0) {
				fread(&b, sizeof(Users), 1, fp);	//依次读取用户并记录用户总数
				N++;
			}
		}
		N = N-1;	//实际用是完成上面循环的 N-1
		fclose(fp);
		Users user[N];		//定义结构体数组
		if((fp=fopen("Users.txt","r"))==NULL) {		// 判断文件是否能打开
			printf ("\t\tCannot open file\n");
			exit(-1);
		} 
		else {
			i=0;
			do {
				fread(&user[i],sizeof(Users),1,fp);	//将用户表中的数据都读入以便修改指定用户的密码
			} while(feof(fp)==0);		//直到读到文件尾结束
		}
		printf("\t\t设置您的新密码:[   \t\t]\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
		i= 0;
		/*
			让输入的密码不可见
		*/
		do {
			user[num].pwd[i]=getch();
			if(user[num].pwd[i]=='\r')
				break;
			if(user[num].pwd[i]=='\b') {
				if(i==0) {
					printf("\a");
					continue;
				}
				i=i-1;
				printf("\b");
			} 
			else {
				i=i+1;
				printf("*");
			}
		} while(user[num].pwd[i]!='\n' && i<20);
		user[num].pwd[i]= '\0';
		printf("\n");
		if ((fp=fopen("Users.txt","w"))==NULL) { 	 /*判断文件是否能打开*/
			printf ("\t\tCannot open file\n");
			exit(0);
		}
		for (i=0; i<N; i++) 	/*将内存中的用户信息输出到磁盘文件中去*/
			if (fwrite(&user[i],sizeof(Users),1,fp)!=1)
				printf("File write error\n");
		fclose(fp);
		printf("\t\t密码修改成功!\n");
		return ;
	} 
	else {
		for(i=0; i<3; i++) {
			printf("\t\t多次输入错误!!%d秒后退出 ……",3-i);
			printf("\r");
			Sleep(1000);
		}
	}
}

运行测试:

注意:运行前需要在程序目录新建一个.txt的用户信息表

  1.注册用户:

(1)正常注册(账号未被注册)

(2)异常注册(账号已被注册)

 2.用户登录:

(1)用户未注册

 (2)用户已注册

        ①密码正确

         ②密码错误

 3.用户修改密码:

(1)无当前用户:

 (2)用户存在

        ①正确输入验证码:

         ③错误输入验证码        

  (3) 修改后登录 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C语言】简易登录注册系统(登录、注册、改密、文件操作) 的相关文章

  • 一个优秀的C#开源绘图软件 DrawTools

    1 Extensions to DrawTools Author Mark Miller I develop software for a leading healthcare system in Northern Illinois Dow
  • jfrog artifactory 的使用

    jfrog artifactory 使用 Artifactory 仓库类型 Artifactory 仓库主要有四种类型 远程仓库 本地仓库 虚拟仓库及分发仓库 分别应用在如下不同的场景 远程仓库 Artifactory 仓库支持代理公网或内
  • WebPlusPro平台之(7)轮播图的导入和制作

    轮播图 WebPlusPro之轮播图 1 定义 2 使用方法 2 1 方法一 2 2 方法二 2 2 1 解决方法一 治标又治本 2 2 2 解决方法二 方便又快捷 2 2 3 解决方法三 前沿又美观 3 结语 WebPlusPro之轮播图
  • thymeleaf基本使用

    简介 thymeleaf是用来代替jsp的模板引擎 可以用th 表达式从后台获取数据 基本语法 th text 用来渲染一个文本 div request设置的msg值 div div session设置的msg div div applic

随机推荐

  • git深入理解(六):git checkout详解

    Switch branches or restore working tree files git checkout h usage git checkout
  • SpringMVC从入门到精通

    一 SpringMVC基础入门 创建一个HelloWorld程序 1 首先 导入SpringMVC需要的jar包 2 添加Web xml配置文件中关于SpringMVC的配置 1 2 3 4 5 6 7 8 9
  • Redis里的key过期了,为什么内存没有释放

    1 Redis过期策略定期删除和惰性删除 定期删除 Redis会每隔一段时间执行一次定期删除 但并不保证所有过期的key会被立即清理掉 惰性删除 当一个key被访问时 Redis会监测key是否过期 过期则删除并释放相应内存空间 2 解答
  • 什么是乐观锁,什么是悲观锁?

    在互联网公司面试中 很多小伙伴都被问到关于锁的理解 今天 我给小伙伴们来聊一聊我对锁的理解 不管我们互斥锁 自旋锁 重入锁 读写锁 行锁 表锁等等等等这些概念 我把他们都归纳为两种类型 乐观锁和悲观锁 彻底讲明白Java中眼花缭乱的各种并发
  • Bun v0.8.0 正式发布,Zig 编写的 JavaScript 运行时

    Bun 是采用 Zig 语言编写的高性能 全家桶 JavaScript 运行时 官方称其为 all in one JavaScript runtime 所谓 all in one 是因为 Bun 提供了打包 转译 安装和运行 JavaScr
  • 【推荐系统->统计学】辛普森悖论(Simpson‘s paradox)

    辛普森悖论 辛普森悖论 Simpson s paradox 也有其他名称 是概率和统计中的一种现象 即一种趋势出现在几组数据中 但当这些组组合在一起时 趋势就会消失或逆转 这个结果在社会科学和医学科学统计中经常遇到 并且当频率数据被过度地给
  • Zookeeper应用场景和底层设计

    一 什么是zookeeper Zookeeper是一个开源的分布式协调服务框架 它是服务于其它集群式框架的框架 简言之 有一个服务A 以集群的方式提供服务 只需要A专注于它提供的服务就可以 至于它如何以多台服务器协同完成任务的事情 交给Zo
  • eventEmitter.addListener与eventEmitter.on有什么区别

    eventEmitter addListener与eventEmitter on有什么区别 在 Node js 中 eventEmitter addListener 和 eventEmitter on 是等效的方法 它们都用于为事件注册监听
  • 面试官问“为什么应聘这个岗位”,应该如何回答?

    面试的时候 我们经常会被问到一个问题 为什么要来应聘这个职位 这是很多同学 不管是在校招还是社招里面都会碰到的一个场景 出现这种问题 一般来说有两种情况 一种是大学专业 或之前的实习经验 工作经验 跟这个职位的相关度不大 第二种情况是 跨越
  • mysql 可以承受多少人_mysql每秒最多能插入多少条数据 ? 死磕性能压测

    前段时间搞优化 最后瓶颈发现都在数据库单点上 问DBA 给我的写入答案是在1W 机械硬盘 左右 联想起前几天infoQ上一篇文章说他们最好的硬件写入速度在2W后也无法提高 SSD硬盘 但这东西感觉从来没证实过 故一时兴起 弄台虚拟机压测起来
  • HDLBits 系列(6)——Sequential Logic(Latches and Flip-Flops)

    目录 3 2 Sequential Logic 3 2 1 Latches and Flip Flops 1 D flip flop 2 D flip flops 3 DFF with reset 4 DFF with reset valu
  • AOP常用的几种增强方式,各自的特点(代码辅助)?

    1 前置增强 又称前置通知 前置增强使用 Befor注解标识 增强方法优先于目标方法执行 前置增强方法 Before execution int mul int int 执行方法之前执行下面的方法 public void before Jo
  • vue使用vant的list组件使用

    模板代码
  • 性能测试—性能测试方案设计思路总结

    一 需求分析 1 测试目的 为什么测 目的在于测试系统相关性能能否满足业务需求 通常分以下两种情况 1 新项目上线 2 老项目优化 如果是老项目优化 可考虑是否存有历史测试方案 如果有可以参考 或许可以省事很多 2 测试对象 要测啥 测试对
  • RK开发板的USB连接(Ubuntu)

    一 安装连接工具 sudo apt get install putty 二 启动putty工具 sudo putty 三 连接usb 并查看相关的信息 查看接入的是否有usb ls dev tty 显示如下 含有usb接口 dev ttyU
  • JVM的GC ROOTS有哪些?

    jvm垃圾回收是根据可达性分析算法来判断堆里面的对象是否有用 可达性分析算法是从GC ROOTS扫描对象引用链 来筛选出有用的对象 扫面完成后 回收掉无用的对象 jvm的GC ROOTS有哪几个地方呢 1 虚拟机栈 局部变量表中引用的对象
  • 寒假训练 第三节 数据结构基础 总结

    栈 基本操作 1 判断栈空 2 判断栈满 3 进栈 4 出栈 判断栈空 操作 s gt top 1 当栈顶指向 1时说明该栈为空 判断栈满 操作 s gt top MAXEN 1 当栈顶为栈长减一时说明栈为满 进栈 s gt top s g
  • react-native之ART绘图详解

    背景 在移动应用的开发过程中 绘制基本的二维图形或动画是必不可少的 然而 考虑到Android和iOS均有一套各自的API方案 因此采用一种更普遍接受的技术方案 更有利于代码的双平台兼容 art是一个旨在多浏览器兼容的Node style
  • PF_INET AF_INET

    http blog csdn net csdn zc article details 7656445 在写网络程序的时候 建立TCP socket sock socket PF INET SOCK STREAM 0 然后再绑定本地地址或连接
  • 【C语言】简易登录注册系统(登录、注册、改密、文件操作)

    概述 本登录注册系统通过使用C语言中的结构体 函数 文件操作以及指针等 设计与实现了一个小型用户登录注册系统的登录 注册 修改密码等基本功能 本系统全部功能基本运行良好 用户界面友好 操作简单 使用方便 但系统仍然有不完善之处 例如在隐藏用