草庐IT

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

.魚肉 2023-04-04 原文

概述:

        本登录注册系统通过使用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) 修改后登录 

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

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  8. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  9. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  10. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

随机推荐