个人项目
第一次接触这个工程项目,感觉问题不大,在看过学长学姐们的代码和自己一边理解一边写(做中学)。第一周,加油!
题目要求
用户:
小学、初中和高中数学老师。
功能:
- 命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;
- 登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;
- 题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5)
- 在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;
- 生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;
- 个人项目9月17日晚上10点以前提交至创新课程管理系统。提交方式:工程文件打包,压缩包名为“几班+姓名.rar”。迟交2天及以内者扣分,每天扣20%。迟交2天及以上者0分。
附表-1:账户、密码
账户类型 | 账户 | 密码 | 备注 |
小学 | 张三1 | 123 | |
张三2 | 123 | ||
张三3 | 123 | ||
初中 | 李四1 | 123 | |
李四2 | 123 | ||
李四3 | 123 | ||
高中 | 王五1 | 123 | |
王五2 | 123 | ||
王五3 | 123 |
附表-2:小学、初中、高中题目难度要求
小学 | 初中 | 高中 | ||
难度要求 | +,-,*./ | 平方,开根号 | sin,cos,tan | |
备注 | 只能有+,-,*./和() | 题目中至少有一个平方或开根号的运算符 | 题目中至少有一个sin,cos或tan的运算符 |
简单一个草图
设计实现
类
class User{
private:
int Number; //题数
string Name; //账号
string Passward; //密码
string Type; // 类型
string folderPath; //文件夹路径
string Path; //路径
ofstream Paper; //创建文件并向文件写入信息
stringstream test; //文件写入
public:
void SignIn(); //登录函数
void Creat_Paper();; //创造文件函数
void Symbol(int option,int ope);//初高中入口
void Switch_Type(); //选择类型函数
void Name_File(); //文件命名
void Init_Directory(string name); //初始化文件夹
bool Check(); //检查函数
void CheckNum(int& n); //限制函数
};
class Admin{
private:
string Name; //账号
string Passward; //密码
string id; //类型
public:
void inquire(); //查询函数
void modify(); //修改函数
void change(); //具体修改
void add(); //添加教师
};
首先设计了两个类,一个是user类,另外一个是admin类。(教师和管理员,管理员来凑数)
并且定义了8个变量和8个函数来实现。
函数
函数1:CheckNum函数
void User::CheckNum(int& n)
{
int i;
string x; //用来接受输入
bool flag = false;
while (cin >> x) {
if(x=="-1") {
cout<<"您已退出,请重新登录!"<<endl<<endl;
SignIn();
break;
}
for (i = 0; i < x.size(); ++i) {
//判断是否为中文
if (x[i] & 0x80) {
cout << "\n输入错误,请重新输入正确的数字: ";
break;
}
//判断是否为字母或者其它字符
if (!isdigit(x[i])) {
cout << "\n输入错误,请重新输入正确的数字: ";
break;
}
}
if (i == x.size()) {
break; //如果字符串中所有字符都合法,则退出while循环
}
}
n = atoi(x.c_str()); //将string字符串转化为整数
}
作用:在输入题的时候检查是否输入数字,而不是做字母或其他符号。
反例子:
函数2:Init_Directory函数
void User::Init_Directory(string name){
std::string prefix = "./题库/"+name+'/';
if (_access(prefix.c_str(), 0) == -1) //如果文件夹不存在
_mkdir(prefix.c_str()); //则创建
}
作用:配合登录函数进行题库的创建,如果不存在文件夹就创建。
函数345:SignIn函数 Name_File函数 Creat_Paper函数
void User::SignIn() { //登录函数
while(1) {
cout<<"请输入用户名和密码:";
cin>>Name>>Passward; //密码 用户名
if(Name=="张三1"||Name=="张三2"||Name=="张三3") {
if(Passward=="123") {
Type="小学";
Init_Directory(Name);
break;
}
} else if(Name=="李四1"||Name=="李四2"||Name=="李四3") {
if(Passward=="123") {
Type="初中";
Init_Directory(Name);
break;
}
} else if(Name=="王五1"||Name=="王五2"||Name=="王五3") {
if(Passward=="123") {
Type="高中";
Init_Directory(Name);
break;
}
}
cout<<"请输入正确的用户名、密码!"<<endl<<endl;
}
cout<<endl<<"*****用户成功登录!*****"<<endl<<endl;
cout<<endl<<"*****当前选择为"<<Type<<"出题*****"<<endl<<endl;
folderPath=".\\题库\\"+Name;
//folderPath="文件路径"+Name;//确定当前账号对应的文件夹的位置
Name_File(); //文件命名
Creat_Paper(); //生出符合输入的数学试卷
}
void User::Creat_Paper() {
cout<<"准备生成"<<Type<<"数学题目..."<<endl;
cout<<"请输入生成题目数量(输入-1将重新登录):";
CheckNum(Number);
while(Number>30||Number<10) {
cout<<"输入题目数量无效,请重新输入:";
CheckNum(Number);
}
Paper.open(Path.c_str());
/*
c_str() 把 Path转变为 char*类型的变量
打开文件
*/
Paper<<"这是一张"<<Type<<"卷子,共有"<<Number<<"道题目"<<endl<<endl;
int n=Number;
while(n!=0) { // 出题过程
n--;
int OpNumber=rand()%5+1; //随机生成操作数的个数
if(OpNumber==1&&Type=="小学") {//小学至少两位操作数
OpNumber=rand()%4+2;
}
int oNum=OpNumber;//保存操作数的个数
int bracket_left=0; //左括号位置
int bracket_num =0;//括号内操作数个数
if(OpNumber>2&&rand()%2==1) { //生成的题目有括号
if(OpNumber==3) {
bracket_left=rand()%2+1;
bracket_num=2;
} else if(OpNumber==4) {
bracket_left=rand()%3+1;
if(bracket_left==3) bracket_num=2;//位后,只能括住两个数
else bracket_num=rand()%2+2; //位前,可以括住2-3个数
} else {
bracket_left=rand()%4+1;
if(bracket_left==4) bracket_num=2;
else if(bracket_left==3) bracket_num=rand()%2+2;
else bracket_num=rand()%3+2;
}
}
bool flag=false;//确保有平方,根号,sin,cos或tan
int sum[]={0,30,45,60,90,120,135,150,15,75,180,360,270};//规定三角函数值范围
while(oNum!=0) {
oNum--;
if(oNum==OpNumber-bracket_left) test<<"(";
int option;
if(Type=="小学") option=0;
else if(Type=="初中") {
option=rand()%3;
if(option!=0) flag=true;//说明已经生成了平方或者根号中的一个了
if(oNum==0&&flag==false) option=rand()%2+1;
} else if(Type=="高中") {
option=rand()%6;
if(option>2) flag=true;//说明已经生成了平sin,cos或tan中的一个了
if(oNum==0&&flag==false) option=rand()%5+1;//说明一直到最后一个操作数都没有sin,cos或tan,则最后一个必须有
}
int Operand=rand()%100+1;
if(option>=3)
{
Operand=sum[rand()%13];
}
Symbol(option,Operand);
if(oNum==OpNumber-bracket_left-bracket_num+1) test<<")";
if(oNum!=0) {
int opt=rand()%4;
switch(opt) {
case 0:
test<<"+";
break;
case 1:
test<<"-";
break;
case 2:
test<<"*";
break;
case 3:
test<<"/";
break;
}
} else test<<"=";
}
test<<endl;
bool check_result=Check();//检查
if(check_result==true) {
int order=Number-n;
Paper<<order<<"、 "<<test.str()<<endl;
test.str(" ");
}
else n++;
}
Paper.close();
}
void User::Name_File() {//给文件命名的函数
char FileName[100];
struct tm *ptr;
time_t It=time(NULL);
ptr=localtime(&It);
strftime(FileName,30,"%y-%m-%d-%H-%M-%S.txt",ptr);//以年月日时分秒对输出文件命名
Path=folderPath+"\\"+Type+FileName;
}
SignIn函数实现登录
create_paper函数实现出题
Name_File函数实现命名
Check函数
bool User::Check() {
std::string checkPath = folderPath + "/Check.txt";//检查文件相对路径
std::ifstream checkFile(checkPath.c_str());//打开检查文件为读模式
std::string line;
while (std::getline(checkFile, line)) {//循环读取检查文件每行到 line
if (line == test.str()) {
return false; //如果读到的行与当前生成题目test.str()相同,返回false,表示重复
}
}
std::ofstream outfile(checkPath.c_str(), std::ios::app);//读取完所有行后没有找到重复,打开检查文件为追加写模式
outfile << test.str() << "\n";//将当前生成题目写入检查文件
return true;
}
Check函数实现检查
存在问题(暂定)
1.在你输入用户时,我前面前置一个空格,你还是能登录进去
2.当我数量输入初中(非数字)的时候,你这个死循环了(已解决)
3.重复要素过多
4.会出现登录不了的情况(偶尔,咱也不知道)
项目模样:
虽然最后还是有bug,但好在给各位少校留一个思路,还请各位少侠手下留情。源代码过段时间za