结对编程队友代码分析


摘要

本博客是对结对编程队友丑怡丹个人项目:中小学生数学题自动生成系统的分析

一、           实现语言

C++

二、           整体思路

1.首先在teacher.h文件里定义存储教师信息的结构体,各种全局符号、数组、函数体的声明。其中五个函数实现主要功能。

void Init();          //录入教师信息

void sign_in();       //登录

void prepare_exam();  //试卷生成

void creat_txt();     //创建文件

void trans_();    //试卷类型切换

 2.在主函数中,各个函数分别实现功能如下:

void Init():

该函数实现录入教师信息的功能,使用文件流,生成TeacherDate.txt的文件,while循环判断读取信息,然后依次录入信息。

void Init()
{
    int  n = 0;
    ifstream inFile( "TeacherDate.txt" );
    while (!inFile.eof() && n < 9)
    {
        inFile >> t[n].type >> t[n].name >> t[n].password;
        n++;
    }      
    inFile.close();
    /*test
    for (int i=0; i<9; i++)
    {
        cout << t[i].type << " " << t[i].name << " " << t[i].password << endl;
    }*/
}

 

void sign_in()

根据题目要求,登录函数初始会显示================================================

===========中小学数学卷子自动生成程序===========

================================================

====请输入用户名和密码(两者之间用空格隔开)====

输入用户名和密码之后用while循环判断输入的用户名与密码与教师信息里的是否匹配。判断输入的题目数量是否吻合。调用试卷生成函数,生成试卷后在set容器里判断查重,遍历所有之前文件。

void sign_in()  //登录 
{
    cout << "================================================" << endl;
    cout << "===========中小学数学卷子自动生成程序===========" << endl;
    cout << "================================================" << endl;
    cout << "====请输入用户名和密码(两者之间用空格隔开)====" << endl;
    cin >> g_name >> g_password;
    
    int i = 0;
    while (i<9)
    {
        if ((g_name==t[i].name) && (g_password==t[i].password))
        {
            t_flag = 1;
            i++;
            break;
        }
        else
            i++;
    }
    if (t_flag == 0)
    {
        cout << "====请输入用户名和密码(两者之间用空格隔开)====" << endl;
    }
    else
    {
        g_type = t[i-1].type;
    } 
}

 

void creat_txt():

创建存储TXT文件的文件夹,存储给每个账户生成的试卷。这里给的路径是固定的,在D://software_engineer//personal_program//exam//”下,用time()函数获取系统时间。

void creat_txt()
{
    //创建文件夹 
    string floder_path = "D://software_engineer//personal_program//exam//"+g_name; 
    if (_access(floder_path.c_str(), 0) == -1)    //如果文件夹不存在
        _mkdir(floder_path.c_str());                //则创建
    //创建txt文件 
    FILE *fp = NULL;
    time_t now;
    char name[100] = {0};
    time(&now);     //获取从1970至今过了多少秒,存入time_t类型的timep
    strftime( name, sizeof(name), "%Y-%m-%d-%H-%M-%S.txt",localtime(&now) ); //最后一个参数是用localtime将秒数转化为struct_tm结构体
    time_path = floder_path+"//"+(string)name;
    if((fp = fopen(time_path.c_str(), "w")) == NULL)
        perror("");
        
    fclose(fp);
}

 

void prepare_exam()

创建文件后,根据操作个数,账号类型,判断生成试卷种类,主体结构用if语句实现。其中使用随机数,随机生成操作数。

void prepare_exam()
{
cout << "==============准备生成"<< g_type <<"数学题目==============" << endl
<< "=====请输入生成题目数量(有效范围为[10,30])====" << endl
<< "=========输入-1将退出当前用户,重新登录=========" << endl; 
while(1) {
cin >> ques_num;
if (ques_num == -1)
{
g_exitflag = 1;
return;
}
else if ((ques_num>=10) && (ques_num<=30))
{
cout << "==============正在生成题目,请稍后==============" << endl;
break;
}
else
{
cout << "======!!!不符合题目数量的有效输入范围!!!=====" << endl
<< "===请重新输入生成题目数量(有效范围为[10,30])==" << endl; 
}
}
creat_txt();
//获取历史题目记录
set <string> ques_set;              //定义set容器缓存历史题目
int tnum = 0;                     //题号
ifstream infile;
infile.open(history_path.c_str());  //打开历史题目文件
string temp_ques;                  //保存单条历史题目
while (infile>>temp_ques) {        //遍历历史记录文件
ques_set.insert(temp_ques);
}
infile.close();
//出题前先打开试卷文件和历史记录文件
ofstream ofile;
ofstream newpaper(time_path.c_str());
ofstream newhistory(history_path.c_str(),ios::app);  
//出题
if (newpaper) {       //试卷输出文件打开成功开始出题   
srand(time(0));   //设置系统时间为随机数种子
while (ques_num) {    //完成一个题目quesnum减1,quesnum大于0时继续循环出题  
int opera_num = rand()%5+1;       //操作数数目,1-5随机数
int brac_num = rand()%5;          //最多括号数目
int distance=0, lnum=0, rnum=0;   //括号距离,左括号数目,右括号数目
int first_flag = 0;                 //开头有为1
int cnt = 0;                      //计数器
string ques_str;                  //生成的题目
string tmp;                       //数字转字符串中介
stringstream str;                 //数字转字符串
if (g_type=="小学")
{
//前opera_num-1个操作数按左括号、操作数、右括号、运算符顺序生成
while ( cnt < opera_num-1) { 
//1/2概率生成左括号
if(rand()%2>0 && lnum<brac_num)
{  
lnum++;
distance = 1;
ques_str+="(";
}
//随机生成1-100的操作数
str << rand()%100+1;
tmp = str.str();       
ques_str+=tmp;
str.str("");         //清空stringstream缓冲区
//1/2概率生成右括号
if(rand()%2>0 && lnum<rnum && distance>1)  //避免括号内只有一个操作数的无效括号
{  
lnum++;
distance=0;      //生成右括号distance清零
ques_str+=")";
}
distance++;
//生成算术符
ques_str+=primarysymbols[rand()%4];
cnt++;
}
//最后一个操作数按操作数、右括号顺序生成
str << rand()%100+1;        //生成操作数
tmp = str.str();
ques_str+=tmp;
str.str("");
while (lnum > rnum) {    //补全右括号
ques_str+=")";
rnum++;
}
}
else if (g_type=="初中")
{
bool square_flag = 0;              //产生平方标志 
int junior_num = rand()%opera_num+1;  //根号和平方根的数量,大于1的随机数
//前opera_num-1个操作数按左括号、根号、操作数、平方、右括号、运算符顺序生成
while (cnt < opera_num-1) {
//1/2概率生成左括号
if (rand()%2>0 && lnum<brac_num)
{ 
lnum++;
distance = 1;
ques_str+="(";
}
//1/2概率当前操作数有根号或平方
if (rand()%2>0 && junior_num>0)
{
junior_num--;
if (rand()%2 > 0)
{
//1/2概率是根号
ques_str+=juniorsymbols[0];                                                        
}
else
{
square_flag = 1;//否则将产生平方
}
}
//生成1-100操作数 
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//squareflag为1产生平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
//1/2概率生成右括号
if (rand()%2>0 && distance>1 && lnum<rnum)
{
rnum++;
distance = 0;
ques_str+=")";
}
distance++;
ques_str+=primarysymbols[rand()%4];
cnt++;
}
//最后一个操作数按根号、操作数、平方、右括号顺序生成
if (junior_num>0)   //前面的操作数没产生完根号/平方
{
//最后一个必定产生,保证至少有一个
if (rand()%2>0)
{
//1/2概率是根号
ques_str+=juniorsymbols[0];
}
else
{
//否则产生平方
square_flag = 1;
}
}
//生成1-100操作数                                                           
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//squareflag为1生成平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
//补全右括号
while (lnum>rnum) {
ques_str+=")";
rnum++;
}
}
else if (g_type=="高中")
{
int cir_num = rand()%opera_num+1;   //三角的数量,大于1的随机数
int square_flag = 0;      //平方标志 
int select = 0;
while (cnt<opera_num-1) {
if (rand()%2>0 && lnum<brac_num)
{
//1/2概率生成左括号
lnum++;
distance = 1;
ques_str+="(";
}
if (rand()%2>0 && cir_num>0)
{
//1/2概率当前操作数有三角
cir_num--;
select = rand()%3;     //sin、cos、tan三选一
switch(select)
{
case 0:ques_str+=seniorsymbols[0]; break;
case 1:ques_str+=seniorsymbols[1]; break;
case 2:ques_str+=seniorsymbols[2]; break;
}
}
if (rand()%20==10)
{
//1/20概率当前操作数有根号或平方根
if (rand()%2>0)
{
//1/2概率是根号
ques_str+=juniorsymbols[0];
}
else
{
square_flag=1;
}
}
//生成1-100操作数
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//若squareflag为1生成平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
if (rand()%2>0 && distance>1 && lnum<rnum)
{
//1/2概率生成右括号
rnum++;
distance=0;
ques_str+=")";
}
distance++;
//生成算数符
ques_str+=primarysymbols[rand()%4];
cnt++;
}
if (cir_num>0)
{
//保证至少有一个三角int select=rand()%3;
switch (select)
{
case 0:ques_str+=seniorsymbols[0]; break;
case 1:ques_str+=seniorsymbols[1]; break;
case 2:ques_str+=seniorsymbols[2]; break;
}
}
if (rand()%20==0)
{
//1/20概率有根号/平方
if(rand()%2==0) //1/2概率为根号
ques_str+=juniorsymbols[0];
else
square_flag=1;  //否则为平方
}        
//生成1-100操作数
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//若squareflag为1生成平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
//末尾补齐右括号
while (lnum>rnum) {
ques_str+=")";
rnum++;
}
}
//去除最外层无效括号
int length = ques_str.length();
if (ques_str[0]=='(' && ques_str[length-1]==')')
{
ques_str = ques_str.substr(1,length-2);
}
ques_str+="=";
if (ques_set.find(ques_str)==ques_set.end())
{
//find返回为set的末尾说明没有找到相同元素,实现去重
ques_set.insert(ques_str);               //题目缓存在历史题库set
tnum++;
newpaper << tnum;
newpaper << ".";
newpaper << ques_str << endl;               //题目加入试卷
newhistory << ques_str << endl;               //题目加入历史题库
ques_num--;
}
}
newpaper.close();
newhistory.close();
}
cout << "===============题目已生成,请查看===============" << endl;
trans_type(); 
}

 

void trans_type()

if语句判断输入为-1时退出登录。其后切换账户类型,主体结构还是用while循环,内嵌if判断语句实现操作。

void trans_type()
{
if (g_exitflag==1)
{
cout << "=============已成功退出,下次再见!=============" << endl; 
cout << "================================================" << endl;
system("cls");
}
cout << "================================================" << endl;
cout << "===========是否需要切换试卷类型(Y/N)==========" << endl;
char choice;
string t_type;
while(1)
{
cin >> choice;
if (choice=='Y')
{
cout << "=====请输入小学、初中和高中三个选项中的一个=====" << endl;
while(1)
{
cin >> t_type;
if (t_type=="小学" || t_type=="初中" || t_type=="高中")
{
g_type = t_type;
break; 
}
else
{
cout << "=====请输入正确的切换类型(小学/初中/高中)=====" << endl;
}
}
prepare_exam();
}
else if (choice=='N')
{
prepare_exam();
return;
}
else
{
cout << "================================================" << endl;
cout << "============请输入正确的选项(Y/N)============" << endl;
}
}
}

 

int main()

主函数中的流程,首先是教师信息初始化,录入,登录与生产文件。If语句判断输入为-1时退出登录。

 

int main()
{
while(1)
{
Init();
sign_in();
prepare_exam();
if (g_exitflag==1)
{
cout << "=============已成功退出,下次再见!=============" << endl; 
cout << "================================================" << endl;
}
}
return 0;
}

三、整体代码

1.teacher.h

#include <string>
using namespace std;
struct Teacher {
string name;
string type;
string password;
};
//运算符定义 
string primarysymbols[]={"+","-","*","/"};
string juniorsymbols[]={"√","^2"};
string seniorsymbols[]={"sin","cos","tan"};
string g_name;
string g_password;
string g_type;
string history_path;  //历史题库文件路径
string time_path;     //txt存放路径 
bool t_flag = 0;      //登陆成功标志位
bool g_exitflag=0;      //退出标志 
signed int ques_num;         //出题数目
void Init();          //录入教师信息
void sign_in();       //登录
void prepare_exam();  //试卷生成 
void creat_txt();     //创建文件
void trans_type();    //试卷类型切换 

main.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <set>
#include <time.h>
#include <stdlib.h>
#include <direct.h>
#include "Teacher.h"
using namespace std;
Teacher t[10];
//导入教师信息
void Init()
{
int  n = 0;
ifstream inFile( "TeacherDate.txt" );
while (!inFile.eof() && n < 9)
{
inFile >> t[n].type >> t[n].name >> t[n].password;
n++;
}      
inFile.close();
/*test
for (int i=0; i<9; i++)
{
cout << t[i].type << " " << t[i].name << " " << t[i].password << endl;
}*/
}
void sign_in()  //登录 
{
cout << "================================================" << endl;
cout << "===========中小学数学卷子自动生成程序===========" << endl;
cout << "================================================" << endl;
cout << "====请输入用户名和密码(两者之间用空格隔开)====" << endl;
cin >> g_name >> g_password;
int i = 0;
while (i<9)
{
if ((g_name==t[i].name) && (g_password==t[i].password))
{
t_flag = 1;
i++;
break;
}
else
i++;
}
if (t_flag == 0)
{
cout << "====请输入用户名和密码(两者之间用空格隔开)====" << endl;
}
else
{
g_type = t[i-1].type;
} 
}
void prepare_exam()
{
cout << "==============准备生成"<< g_type <<"数学题目==============" << endl
<< "=====请输入生成题目数量(有效范围为[10,30])====" << endl
<< "=========输入-1将退出当前用户,重新登录=========" << endl; 
while(1) {
cin >> ques_num;
if (ques_num == -1)
{
g_exitflag = 1;
return;
}
else if ((ques_num>=10) && (ques_num<=30))
{
cout << "==============正在生成题目,请稍后==============" << endl;
break;
}
else
{
cout << "======!!!不符合题目数量的有效输入范围!!!=====" << endl
<< "===请重新输入生成题目数量(有效范围为[10,30])==" << endl; 
}
}
creat_txt();
//获取历史题目记录
set <string> ques_set;              //定义set容器缓存历史题目
int tnum = 0;                     //题号
ifstream infile;
infile.open(history_path.c_str());  //打开历史题目文件
string temp_ques;                  //保存单条历史题目
while (infile>>temp_ques) {        //遍历历史记录文件
ques_set.insert(temp_ques);
}
infile.close();
//出题前先打开试卷文件和历史记录文件
ofstream ofile;
ofstream newpaper(time_path.c_str());
ofstream newhistory(history_path.c_str(),ios::app);  
//出题
if (newpaper) {       //试卷输出文件打开成功开始出题   
srand(time(0));   //设置系统时间为随机数种子
while (ques_num) {    //完成一个题目quesnum减1,quesnum大于0时继续循环出题  
int opera_num = rand()%5+1;       //操作数数目,1-5随机数
int brac_num = rand()%5;          //最多括号数目
int distance=0, lnum=0, rnum=0;   //括号距离,左括号数目,右括号数目
int first_flag = 0;                 //开头有为1
int cnt = 0;                      //计数器
string ques_str;                  //生成的题目
string tmp;                       //数字转字符串中介
stringstream str;                 //数字转字符串
if (g_type=="小学")
{
//前opera_num-1个操作数按左括号、操作数、右括号、运算符顺序生成
while ( cnt < opera_num-1) { 
//1/2概率生成左括号
if(rand()%2>0 && lnum<brac_num)
{  
lnum++;
distance = 1;
ques_str+="(";
}
//随机生成1-100的操作数
str << rand()%100+1;
tmp = str.str();       
ques_str+=tmp;
str.str("");         //清空stringstream缓冲区
//1/2概率生成右括号
if(rand()%2>0 && lnum<rnum && distance>1)  //避免括号内只有一个操作数的无效括号
{  
lnum++;
distance=0;      //生成右括号distance清零
ques_str+=")";
}
distance++;
//生成算术符
ques_str+=primarysymbols[rand()%4];
cnt++;
}
//最后一个操作数按操作数、右括号顺序生成
str << rand()%100+1;        //生成操作数
tmp = str.str();
ques_str+=tmp;
str.str("");
while (lnum > rnum) {    //补全右括号
ques_str+=")";
rnum++;
}
}
else if (g_type=="初中")
{
bool square_flag = 0;              //产生平方标志 
int junior_num = rand()%opera_num+1;  //根号和平方根的数量,大于1的随机数
//前opera_num-1个操作数按左括号、根号、操作数、平方、右括号、运算符顺序生成
while (cnt < opera_num-1) {
//1/2概率生成左括号
if (rand()%2>0 && lnum<brac_num)
{ 
lnum++;
distance = 1;
ques_str+="(";
}
//1/2概率当前操作数有根号或平方
if (rand()%2>0 && junior_num>0)
{
junior_num--;
if (rand()%2 > 0)
{
//1/2概率是根号
ques_str+=juniorsymbols[0];                                                        
}
else
{
square_flag = 1;//否则将产生平方
}
}
//生成1-100操作数 
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//squareflag为1产生平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
//1/2概率生成右括号
if (rand()%2>0 && distance>1 && lnum<rnum)
{
rnum++;
distance = 0;
ques_str+=")";
}
distance++;
ques_str+=primarysymbols[rand()%4];
cnt++;
}
//最后一个操作数按根号、操作数、平方、右括号顺序生成
if (junior_num>0)   //前面的操作数没产生完根号/平方
{
//最后一个必定产生,保证至少有一个
if (rand()%2>0)
{
//1/2概率是根号
ques_str+=juniorsymbols[0];
}
else
{
//否则产生平方
square_flag = 1;
}
}
//生成1-100操作数                                                           
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//squareflag为1生成平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
//补全右括号
while (lnum>rnum) {
ques_str+=")";
rnum++;
}
}
else if (g_type=="高中")
{
int cir_num = rand()%opera_num+1;   //三角的数量,大于1的随机数
int square_flag = 0;      //平方标志 
int select = 0;
while (cnt<opera_num-1) {
if (rand()%2>0 && lnum<brac_num)
{
//1/2概率生成左括号
lnum++;
distance = 1;
ques_str+="(";
}
if (rand()%2>0 && cir_num>0)
{
//1/2概率当前操作数有三角
cir_num--;
select = rand()%3;     //sin、cos、tan三选一
switch(select)
{
case 0:ques_str+=seniorsymbols[0]; break;
case 1:ques_str+=seniorsymbols[1]; break;
case 2:ques_str+=seniorsymbols[2]; break;
}
}
if (rand()%20==10)
{
//1/20概率当前操作数有根号或平方根
if (rand()%2>0)
{
//1/2概率是根号
ques_str+=juniorsymbols[0];
}
else
{
square_flag=1;
}
}
//生成1-100操作数
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//若squareflag为1生成平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
if (rand()%2>0 && distance>1 && lnum<rnum)
{
//1/2概率生成右括号
rnum++;
distance=0;
ques_str+=")";
}
distance++;
//生成算数符
ques_str+=primarysymbols[rand()%4];
cnt++;
}
if (cir_num>0)
{
//保证至少有一个三角int select=rand()%3;
switch (select)
{
case 0:ques_str+=seniorsymbols[0]; break;
case 1:ques_str+=seniorsymbols[1]; break;
case 2:ques_str+=seniorsymbols[2]; break;
}
}
if (rand()%20==0)
{
//1/20概率有根号/平方
if(rand()%2==0) //1/2概率为根号
ques_str+=juniorsymbols[0];
else
square_flag=1;  //否则为平方
}        
//生成1-100操作数
str << rand()%100+1;
tmp = str.str();
ques_str+=tmp;
str.str("");
//若squareflag为1生成平方
if (square_flag)
{
ques_str+=juniorsymbols[1];
square_flag=0;
}
//末尾补齐右括号
while (lnum>rnum) {
ques_str+=")";
rnum++;
}
}
//去除最外层无效括号
int length = ques_str.length();
if (ques_str[0]=='(' && ques_str[length-1]==')')
{
ques_str = ques_str.substr(1,length-2);
}
ques_str+="=";
if (ques_set.find(ques_str)==ques_set.end())
{
//find返回为set的末尾说明没有找到相同元素,实现去重
ques_set.insert(ques_str);               //题目缓存在历史题库set
tnum++;
newpaper << tnum;
newpaper << ".";
newpaper << ques_str << endl;               //题目加入试卷
newhistory << ques_str << endl;               //题目加入历史题库
ques_num--;
}
}
newpaper.close();
newhistory.close();
}
cout << "===============题目已生成,请查看===============" << endl;
trans_type(); 
}
void creat_txt()
{
//创建文件夹 
string floder_path = "D://software_engineer//personal_program//exam//"+g_name; 
if (_access(floder_path.c_str(), 0) == -1)    //如果文件夹不存在
_mkdir(floder_path.c_str());                //则创建
//创建txt文件 
FILE *fp = NULL;
time_t now;
char name[100] = {0};
time(&now);     //获取从1970至今过了多少秒,存入time_t类型的timep
strftime( name, sizeof(name), "%Y-%m-%d-%H-%M-%S.txt",localtime(&now) ); //最后一个参数是用localtime将秒数转化为struct_tm结构体
time_path = floder_path+"//"+(string)name;
if((fp = fopen(time_path.c_str(), "w")) == NULL)
perror("");
fclose(fp);
}
void trans_type()
{
if (g_exitflag==1)
{
cout << "=============已成功退出,下次再见!=============" << endl; 
cout << "================================================" << endl;
system("cls");
}
cout << "================================================" << endl;
cout << "===========是否需要切换试卷类型(Y/N)==========" << endl;
char choice;
string t_type;
while(1)
{
cin >> choice;
if (choice=='Y')
{
cout << "=====请输入小学、初中和高中三个选项中的一个=====" << endl;
while(1)
{
cin >> t_type;
if (t_type=="小学" || t_type=="初中" || t_type=="高中")
{
g_type = t_type;
break; 
}
else
{
cout << "=====请输入正确的切换类型(小学/初中/高中)=====" << endl;
}
}
prepare_exam();
}
else if (choice=='N')
{
prepare_exam();
return;
}
else
{
cout << "================================================" << endl;
cout << "============请输入正确的选项(Y/N)============" << endl;
}
}
}
int main()
{
while(1)
{
Init();
sign_in();
prepare_exam();
if (g_exitflag==1)
{
cout << "=============已成功退出,下次再见!=============" << endl; 
cout << "================================================" << endl;
}
}
return 0;
}

四、优缺点分析

1.优点:

该代码基于C++编程,是我们比较熟悉的,代码逻辑清醒,容易读懂理解。功能全部实现的同时,在用户信息登录的问题上,不仅仅只是利用if循环,利用文件流实现了多组用户信息的录入,不更改代码的基础上,操作更便捷。

2.缺点:并未基于UTF-8编码。同时生成的文件路径是绝对的,不能灵活更改。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/289372.html

(0)
上一篇 2022年9月14日
下一篇 2022年9月14日

相关推荐

发表回复

登录后才能评论