HNU结对编程队友zzs代码分析

结对编程队友代码分析

一、思路

zzs我的神,拜读一下

使用C++语言进行编程,总体写在一个cpp里。

采用C++中的类,定义属性和成员函数,不同成员函数负责不同部分的功能实现,最后由主函数调用各个成员函数完成用户需求

二、代码实现

1.类(User)的定义
//类:用户
//属性和成员函数 
class User {
	private:
		string upassword_;//用户密码 
		int qnumber_;//题目数量 
		string uname_;//用户姓名,文件夹命名 
		string type_;//小学、初中、高中 
		string folderpath_;//除了以姓名命名的文件夹的前半段路径 
		string path_;//获取姓名后的完整路径 
		ofstream paper_;
		stringstream question_;
	public:
		void Usign_In();//登录界面 
		void Prod_Quiz();//生成试卷 
		void Grade(int option,int ope);//根据option确定不同阶段,由此决定是否使用高级运算符 
		void Switch_Type();//切换试卷类型 
		void Uname_File();//试卷命名 
		bool Check();//每个算式逐个检查与之前出过的是否重复 
};
2.用户登录函数Usign_In()

**这里在用户输入登录信息的时候设置了纠错功能:如果用户只输入了一个内容(以空格判定),系统会提示"-----请您确保用户名和密码都输入哦-----";如果系统输入了三个及以上的内容,系统会提示"-----只需输入用户名和密码哦,请不要输入多余的部分-----"。

**当用户输入了合法但是不存在的用户名时,系统会提示"-----啊哦!该用户不存在!-----",并要求重新输入用户名和密码;当用户名输入正确,但密码不正确时,系统会提示"-----请确认您的密码-----",并要求重新输入用户名和密码。

void User::Usign_In() { //用户登录函数
	while(1) {
		cout<<"请输入用户名和密码(用空格间隔):";
		string whole;
	    getline(cin,whole);
	    int pos=whole.find(" ");
		vector<string> format_check;		
	
		if(pos==string::npos){
			cout<<endl<<"-----请您确保用户名和密码都输入哦-----"<<endl<<endl;
			continue;
		}
		
		int pos2=whole.find(" ",pos+1);
		if(pos2!=string::npos){
			cout<<endl<<"-----只需输入用户名和密码哦,请不要输入多余的部分-----"<<endl<<endl;
			continue;
		}
	    //用空格来划分输入的整个字符串,第一个空格前面则是用户名,后面则是密码
		format_check.push_back(whole.substr(0,pos));
		format_check.push_back(whole.substr(pos+1));

		uname_=format_check[0];
		upassword_=format_check[1]; 
		if(uname_=="张三1"||uname_=="张三2"||uname_=="张三3") {
			if(upassword_=="123") {
				type_="小学";
				break;
			}
			else if(upassword_!="123"){
				cout<<endl;
				cout<<"-----请确认您的密码-----"<<endl<<endl; 
			}
		} else if(uname_=="李四1"||uname_=="李四2"||uname_=="李四3") {
			if(upassword_=="123") {
				type_="初中";
				break;
			}
			else if(upassword_!="123"){
				cout<<endl;
				cout<<"-----请确认您的密码-----"<<endl<<endl; 
			}
		} else if(uname_=="王五1"||uname_=="王五2"||uname_=="王五3") {
			if(upassword_=="123") {
				type_="高中";
				break;
			}
			else if(upassword_!="123"){
				cout<<endl;
				cout<<"-----请确认您的密码-----"<<endl<<endl; 
			}
		} 
		else{
			cout<<endl;
			cout<<"-----啊哦!该用户不存在!-----"<<endl<<endl;
	    	cout<<"-----请输入正确的用户名、密码!-----"<<endl<<endl;
		}
	}
	cout<<endl<<"-----当前选择为"<<type_<<"出题-----"<<endl<<endl;
	folderpath_="C:\\Users\\随行佯醉\\Desktop\\person_proj\\"+uname_;//确定当前账号对应的文件夹的位置
	Uname_File();//文件命名
	Prod_Quiz();//生出符合输入的数学试卷
	Switch_Type();
}
3.生成试卷函数Prod_Quiz()

**使用字符串存储用户输入的题目数量,防止整型溢出导致终端陷入死循环,因为需求是大于三十道就不予出卷了,所以这里直接对字符串要求长度不得超过三位,一旦超过,系统提示“您输入的数据不合法哦”。

**使用cin.clear()和cin.sync()来清空缓存区,防止终端程序将用户输入-1后的回车键误当作用户的输入,避免不必要的纠错提示。

**提示语人性化:当用户输入的题目数量少于十道时,系统提示“题目数量太少了哦,多练习才能取得优异的成绩!”;当用户输入的题目数量多于三十道时,系统会提示“题目数量太多了哦,适度练习可能事半功倍!”。

**括号打印位置的确定方法:用左括号和括住的操作数个数来推出右括号的位置,其中操作数的个数为3到5时,右括号的位置需要分类讨论。

**调用Grade()函数生成除了除了加减乘除运算外的高级运算,通过choice区分是否有可能涉及到高年级题目。

void User::Prod_Quiz() {
	cout<<endl<<"-----准备生成"<<type_<<"数学题目-----"<<endl<<endl;
	cout<<endl<<"请输入生成题目数量(输入-1将退出当前用户,重新登录):";
	string legal_num;
	cin>>legal_num;
	if(legal_num.length()>3){
		cout<<endl;
		cout<<"-----您输入的数据不合法哦-----"<<endl<<endl;
		cout<<"-----再来一次-----"<<endl<<endl; 
		Prod_Quiz(); 
	} 
	else{
		qnumber_=atoi(legal_num.c_str()); //类型转换 
	}
	while(qnumber_>30||qnumber_<10) {
		if(qnumber_==-1) {
			cout<<endl<<"-----您已退出,请重新登录!-----"<<endl<<endl;
			cin.clear();
			cin.sync();
			Usign_In();
			break;
		}
		//cout<<"输入的题目数量无效,请重新输入:";
		else if(qnumber_<10){
			cout<<endl;
			cout<<"*******题目数量太少了哦,多练习才能取得优异的成绩!*******"<<endl<<endl;
			cout<<"请您重新输入题目数量:"; 
		}
		else if(qnumber_>30){
			cout<<endl;
			cout<<"*********题目数量太多了哦,适度练习可能事半功倍!*********"<<endl<<endl;
			cout<<"请您重新输入题目数量:"; 
		}
		string legal_num;
		cin>>legal_num;
		if(legal_num.length()>3){
			cout<<endl;
			cout<<"-----您输入的数据不合法哦-----"<<endl<<endl;
			cout<<"-----再来一次-----"<<endl<<endl; 
			Prod_Quiz(); 
	} 
	    else{
			qnumber_=atoi(legal_num.c_str()); //类型转换 
		}
	}

	paper_.open(path_.c_str());
	paper_<<"这是一张"<<type_<<"卷子,共有"<<qnumber_<<"道题目。"<<endl<<endl;
	
	int n=qnumber_;
	while(n!=0) {//每道题的出题过程,循环Number次即生成Number个题目 
		n--;
		int Num=rand()%5+1;//随机生成操作数的个数
		if(Num==1&&type_=="小学") {//小学至少是两位操作数
			Num=rand()%4+2;//生成2-5个操作数
		}
		int oNum=Num;//保存操作数的个数
		int bracket_left=0;//左括号的位置(此处仅针对只有一对括号的情况)
		int bracket_num=0;//括号括住的操作数的个数 
		if(Num>2&&rand()%2==1) { //生成的题目有括号
			bracket_left=(rand()%(Num-1))+1;//用算式统一左括号位置的计算方法 
		    //bracket_right=bracket_left+3+rand()%(Num-bracket_left);//跟左括号很类似,相当于把右括号可能出现的最左端的位置看作左括号的初始位置带入左括号的运算思路,右括号可能出现的最右位置是操作数加二 
		    if(Num==3) {
				bracket_num=2;
			} else if(Num==4) {
				if(bracket_left==3) bracket_num=2;//位于最后,最多只能括住两个数
				else bracket_num=rand()%2+2;//位于前面,可以括住2-3个数
			} else {
				if(bracket_left==4) bracket_num=2;//只能括住两个的情况
				else if(bracket_left==3) bracket_num=rand()%2+2;//可以括住2-3个的情况
				else bracket_num=rand()%3+2;//可以括住2-4个的情况
			}
		}
		bool flag=false;//确保初中的每道题目中都至少有平方或者根号,高中的每道题都至少有sin,cos或tan中的一个
		while(oNum!=0) {
			oNum--;
			if(oNum==Num-bracket_left) question_<<"(";
			int choice;//通过choice区分是否有可能涉及到高年级题目 
			if(type_=="小学") choice=0;//小学直接输出原数 
			else if(type_=="初中") {
				choice=rand()%3;
				if(choice!=0) flag=true;//说明已经生成了平方或者根号中的一个了
				if(oNum==0&&flag==false)  choice=rand()%2+1;
			} else if(type_=="高中") {
				choice=rand()%6;
				if(choice>2) flag=true;//说明已经生成了平sin,cos或tan中的一个了
				if(oNum==0&&flag==false)  choice=rand()%3+3;//说明一直到最后一个操作数都没有sin,cos或tan,则最后一个必须有
			}
			int Ope_rand=rand()%100+1;//操作数取值范围为1-100 
			Grade(choice,Ope_rand);//生成除了除了加减乘除运算外的高级运算,通过choice区分是否有可能涉及到高年级题目 
			if(oNum==Num-bracket_left-bracket_num+1) question_<<")";
			if(oNum!=0) {
				int cho=rand()%4;
				if(cho==0){
					question_<<"+";
				}
				else if(cho==1){
					question_<<"-";
				}
				else if(cho==2){
					question_<<"*";
				}
				else if(cho==3){
					question_<<"/";
				}
			} 
			else question_<<"=";//算式写完 
		}
		question_<<endl;//换行写下一个算式 
		bool check_result=Check();//检查
		if(check_result==true) {
			int order=qnumber_-n;
			paper_<<order<<"、 "<<question_.str()<<endl;
			question_.str(" ");
		} else n++;
	}
	paper_.close();
}
4.运算符分层函数Grade()

**跟Prod_Quiz()联系起来,根据option确定不同阶段,由此决定是否使用高级运算符

void User::Grade(int choice,int cho) {//对每个操作数进行处理
    if(choice==0){//不处理,即小学的题目
    	question_<<cho;
	}
	else if(choice==1){
		question_<<cho<<"^2";
	}
	else if(choice==2){//choice为0-2时,表示生成初中的题目
		question_<<"√"<<cho;
	}
	else if(choice==3){
		question_<<"sin"<<cho;
	}
	else if(choice==4){
		question_<<"cos"<<cho;
	}
	else if(choice==5){//choice为0-5时,生成的是高中的题目
		question_<<"tan"<<cho;
	}
}
5.切换试卷类型函数Switch_Type()

**切换类型时无需退出用户登录,提示语如下:

-----您还可以继续出卷-----

以下为类型切换选项
若需要切换类型 ---请输入"切换为XX"(小学|初中|高中)---
若不需要 ---输入"否"---
若退出登录 ---请输入"-1"---
请输入:

void User::Switch_Type() { //切换类型选项
	string Typeswitch;
	while(1) {
		cout<<endl<<"-----您还可以继续出卷-----"<<endl<<endl; 
		cout<<endl<<"以下为类型切换选项"<<endl;
		cout<<"若需要切换类型        ---请输入\"切换为XX\"(小学|初中|高中)---" <<endl;
		cout<<"若不需要              ---输入\"否\"---"<<endl;
		cout<<"若退出登录            ---请输入\"-1\"---"<<endl;
		cout<<"请输入:";
		cin>>Typeswitch;
		if(Typeswitch=="否"||Typeswitch=="") {
			Uname_File();
			Prod_Quiz();//生出符合输入的数学试卷
			continue;
		} 
		else if(Typeswitch=="-1"){
			cout<<endl;
			cin.clear();
			cin.sync();
			//system("cls");
			Usign_In();
			//Switch_Type();
		}
		else if(Typeswitch=="切换为小学"||Typeswitch=="切换为初中"||Typeswitch=="切换为高中") {
			type_=Typeswitch.erase(0,6);
			Uname_File();
			Prod_Quiz();//生出符合输入的数学试卷
			continue;
		} 
		else {
			while(1) {
				cout<<endl;
				cout<<"请输入小学、初中和高中三个选项中的一个:";
				cin>>Typeswitch;
				if(Typeswitch=="小学"||Typeswitch=="初中"||Typeswitch=="高中") {
					type_=Typeswitch;
					Uname_File();
					Prod_Quiz();//生出符合输入的数学试卷
					cout<<endl;
					cin.clear();
					cin.sync();
					Switch_Type();
				} 
				else if(Typeswitch=="-1"){
					cout<<endl;
					//system("cls");
					cin.clear();
					cin.sync();
					Usign_In();
					//Switch_Type();
				}
			}
		}
		if(Typeswitch=="-1"){
			cout<<endl;
			//system("cls");
			cin.clear();
			cin.sync();
			Usign_In();
			//Switch_Type();
		}
	}
	cout<<endl<<"-----您已退出本次登录!-----"<<endl;
}
6.查重函数Check()

**每个账号中都有一个check文档,该文档保存该账号生成的所有题目,如果有重复,返回false;如果均不重复,输出。

bool User::Check() {//查重
	string CheckPath=folderpath_+"\\Check.txt";//每个账号中都有一个check文档,该文档保存该账号生成的所有题目
	fstream CheckFile;
	CheckFile.open(CheckPath.c_str(),ios::in|ios::app);
	string temp;
	while(getline(CheckFile,temp)) {
		stringstream stemp(temp);//将掺有数字的temp全部转化,成为一个完整的字符串stemp 
		if(stemp==question_) {
			return false;
		}
	}
	CheckFile<<question_.str();
	CheckFile.close();
	return true;
}
7.试卷命名函数Uname_File()
void User::Uname_File() {//给文件命名的函数
	char fileuname[100];
	struct tm *ptr;
	time_t It=time(NULL);
	ptr=localtime(&It);
	strftime(fileuname,30,"%y-%m-%d-%H-%M-%S.txt",ptr);//以年月日时分秒对输出文件命名
	path_=folderpath_+"\\"+type_+fileuname;
}

三、原则

**如果不是用户主动关闭程序运行,任何一个-1的退出登录都不应该致使程序终断。

**在用户输入之前均有引导语,用户输入之后的纠错也有对应的提示语,不应让用户对即将输入的内容感到迷茫。

四、优缺点

1.优点

用户的需求全部满足;提示语的完整使得程序与用户交互过程很流畅;纠错功能避免溢出导致的终端陷入死循环,同时允许用户一定程度上的非法输入。

2.缺点

每个用户的文件夹时事先建好的,所以在这个代码里试卷的生成路径是绝对路径,不够灵活。

posted on 2022-09-14 00:44  227岁的Nova  阅读(221)  评论(0编辑  收藏  举报