【python】HNU—个人项目—小初高数学学习软件

大三上学期的课程个人项目

一、项目说明

1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;

2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;

3、题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5);

4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;

5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;

二、程序说明

此项目由python语言编写,
包含一个py文件和一个txt文件,
程序运行时两个文件需在同一文件夹下;
在这里插入图片描述
user.txt文件格式如下
【python】HNU—个人项目—小初高数学学习软件_第1张图片

三、程序主要分为用户登录模块和出题模块;

1. 用户登录模块

由两个函数组成,init_user()函数和login_in()函数。
init_user()函数初始化用户信息,将用户名,密码,年级存到列表中作为用户信息列表;
login_in()函数判断用户登录是否成功。

2. 出题模块

主要由set_questions()函数构成,此函数分为四个模块,前三个为出题模块,最后一个是查重模块。
前三个出题模块依次是小学题目、初中题目和高中题目;
查重模块实现原理是将所在文件夹目录下的所有题目加载到列表中,依次查看每一道生成的题目是否与列表中的题目重复;

程序从用户登录开始一直到题目出完并写到txt文件中,全部过程在main函数的一个循环中完成。

四、程序流程

用户登录,用户名和密码输入不正确则重新输入,输入正确则进行下一步;若输入的是生成题目的数量,则开始生成题目并写入到对应用户文件夹下的对应年级下的以实时时间命名的txt文件中;
若输入的是-1,则退出当前用户登录,重新登录;
若输入的是"切换到XX",则先更新当前用户的年级,再重新输入,判断方法同上述三种;
生成题目后退出当前用户,重新输入用户名和密码以重复上述流程

五、exam.py

import random
import re
import os
import time

username = []
password = []
grade = []
grade_flag = 0
gather = []  # 题目集合

'''功能:初始化用户信息
    从文件夹中读取用户信息(用户名、密码和年级)存到列表中
'''
def init_user():
    user=[]
    with open('User.txt', encoding='gb2312') as file_obj: # 使用国际码编码,且单斜杠改为双斜杠
        user = file_obj.readlines()
    for i in range(0, len(user), 3):
        username.append(user[i][4:-1])
        password.append(user[i+1][3:-1])
        grade.append(user[i+2][3:-1])

'''功能:用户登录
    函数参数为输入的用户名、密码、是否登陆成功标志
    返回值prime为是否登陆成功标志,grade_flag为年级标志
'''
def login_in(user_name,user_password,prime):
    for i in range(0, len(username)):
        global grade_flag  # 如果确定要引用全局变量,并且要对它修改,必须加上global关键字。
        if user_name == username[i] and user_password==password[i]:
            if grade[i] == "小学":
                grade_flag = 0
            elif grade[i] == "初中":
                grade_flag = 1
            elif grade[i] == "高中":
                grade_flag = 2
            prime = i
            break
    if prime == -1:
        print("请输入正确的用户名和密码")
    return prime, grade_flag

'''功能:判断输入的是数字(-1或者题目数量)还是年级切换指令
    flag为传进来的参数
    如果输入的是数字,那么返回值为输入的数字、flag(0,说明是数字)、-1(说明不是切换年级指令)
    如果输入的是切换指令,那么返回的是切换到年级、flag(1,说明是切换指令)、年级标志
'''
def number_or_string(flag):
    while 1:
        count = input()
        temp = count
        try:
            float(temp)
            flag = 0 # 能成功转换为浮点型,则是数字
            return count, flag, -1
        except:
            flag = 1 # 不能成功转换为浮点型,则不是数字

        if flag == 1:
            if count=='切换为初中':
                print("准备生成初中数学题目,请输入生成题目数量")
                return '初中', flag, 1
            elif count=='切换为高中':
                print("准备生成高中数学题目,请输入生成题目数量")
                return '高中', flag, 2
            elif count=='切换为小学':
                print("准备生成小学数学题目,请输入生成题目数量")
                return '小学', flag, 0
            else:
                print("请输入小学、初中和高中三个选项中的一个")
                continue

'''功能:创建文件夹
    若文件夹已存在……
    若不存在则创建
'''
def mkdir(path):
    path = path.strip()         # 去除首位空格
    path = path.rstrip("\\")    # 去除尾部 \ 符号
    isExists = os.path.exists(path) # 判断路径是否存在 存在True 不存在False
    if not isExists:   # 如果不存在则创建目录
        os.makedirs(path)
        return True
    else:               # 如果目录存在则不创建
        return False

'''功能:读取当前文件夹下所有已生成题目到列表中
'''
def read_questions(path1):
    files = os.listdir(path1)
    text = []
    for file in files: # 遍历文件夹
        if not os.path.isdir(file): # 判断是否是文件夹,不是文件夹才打开
            f = os.path.basename(file)
            paths=path1+'\\'+f  # 确定文件路径
            with open(paths, encoding='utf-8')as f:
                text.extend(f.readlines())  # 注意extend 和 append的区别
    '''格式化题目'''
    for tex in text:
        text.remove('\n')
    for i in range(0, len(text)):
        text[i] = text[i][text[i].index('.') + 2: text[i].index('\n')]
    return text

'''功能:随机化生成题目
    变量:  题目数量count,
            年级(整型)level,
            用户姓名(用来确定文件夹名称)user_name,
            年级(字符串,用来确定文件夹名称)grade_level
'''
def set_questions(count, level, user_name, grade_level):
    current_time = str(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()))  # 格式化当前时间
    path = user_name + '\\' + grade_level   # 举例: 张三\\初中
    mkdir(path)                             # 创建或打开文件夹
    global gather
    gather = read_questions(path)           # 读取当前文件夹下所有文件内的题目到gather数组中以便查重
    #print(gather)
    path = path + '\\' + current_time + ".txt"  # 设置路径
    data = open(path, "w", encoding='utf-8')    # 打开文件

    qid = 0   #题号
    while qid < count:
        operate_count = random.randint(1, 5)  # 题目操作数个数 最多为5个操作符,最少为1个
        operate_number = []  # 题目操作数范围1--100
        for i in range(1, 101):
            operate_number.append(i)
        basic_symble = ['+', '-', '*', '/']  # 基本操作符加减乘除
        question = str(operate_number[random.randint(0, 99)])  # 随机生成题目的第一个数字
        question = question + basic_symble[random.randint(0, 3)]  # 生成题目的操作符(random.randint(a, b)范围包含a和b)
        question = question + str(operate_number[random.randint(0, 99)])  # 生成题目的操作数
        for i in range(0, operate_count - 1):
            bracket = random.randint(0, 1)  # 随机化是否为此多项式加括号
            if bracket == 1:
                question = '(' + question + ')'
            left_or_right = random.randint(0, 1)  # 随机化添加操作符和数字到多项式的左边还是右边
            if left_or_right == 0:
                question = basic_symble[random.randint(0, 3)] + question  # 生成题目的操作符(random.randint(a, b)范围包含a和b)
                question = str(operate_number[random.randint(0, 99)]) + question  # 生成题目的操作数
            else:
                question = question + basic_symble[random.randint(0, 3)]  # 生成题目的操作符(random.randint(a, b)范围包含a和b)
                question = question + str(operate_number[random.randint(0, 99)])  # 生成题目的操作数
        ''' 
            在小学题目的基础上生成初中题目
            要求是至少有一个平方或开根号运算
        '''
        if level == 1 or level == 2:
            location = random.randint(1, operate_count)  # 至少有一个平方或开根号,先确定位置
            loc = 0
            for c in range(0, len(question)):
                if question[c] in basic_symble:  # 从头开始找确定的location
                    loc += 1
                if loc == location:  # 找到位置
                    square_or_root = random.randint(0, 1)  # 随机生成  平方还是根号
                    if square_or_root == 0:
                        if question[c - 1] != "²":  # 不生成平方的平方
                            question = question[:c] + '²' + question[c:]
                    elif square_or_root == 1:
                        question = question[:c + 1] + '√' + question[c + 1:]
                    break;
            for c in range(0, len(question)):
                if question[c] in basic_symble:  # 如果发现了基本操作符中的一个
                    is_square_and_root = random.randint(0, 1)  # 随机生成 是否添加平方或根号
                    if is_square_and_root == 1:
                        square_or_root = random.randint(0, 1)  # 随机生成  平方还是根号
                        if square_or_root == 0:
                            if question[c - 1] != "²":  # 不生成平方的平方
                                question = question[:c] + '²' + question[c:]
                        elif square_or_root == 1:
                            question = question[:c + 1] + '√' + question[c + 1:]
            ''' 
                在小学题目的基础上生成初中题目
                要求是至少有一个平方或开根号运算
            '''
            if level == 2:
                trig_location = random.randint(1, operate_count)
                trig_loc = 0
                updata_symble = ['+', '-', '*', '/', '(', '√']
                trig = ["sin", "cos", "tan"]
                for c in range(0, len(question)):
                    if question[c] in updata_symble:  # 从头开始找确定的location
                        trig_loc += 1
                    if trig_loc == trig_location:  # 找到位置
                        question = question[:c + 1] + trig[random.randint(0, 2)] + question[c + 1:]
                        break
                for c in range(0, len(question)):
                    if question[c] in updata_symble:  # 从头开始找确定的location
                        is_trig = random.randint(0, 1)
                        if is_trig == 1 and (
                                question[c + 1] != 's' and question[c + 1] != 'c' and question[c + 1] != 't'):
                            question = question[:c + 1] + trig[random.randint(0, 2)] + question[c + 1:]

        '''查重'''
        repeat = 0  # 是否重复的标志,初始化为未重复
        for que in gather:  # 遍历题目集合中的每一道题
            if question == que:  # 如果发现有重复的
                repeat = 1  # 标志为有重复
                break  # 并且不用再继续遍历,跳出次循环即可
        if repeat == 1:  # 如果此题目是重复的
            continue  # 不执行以下操作

        '''如果生成的该题目没有重复,那么执行下面的操作'''
        qid += 1  # 题号+1
        gather.append(question)  # 该题目收入题目集合
        print(str(qid) + '. ' + question + '\n', file=data)  # 并把该题目输出在.txt文件中
        gather.append(question)
    data.close()

def main():
    init_user()
    while 1:
        prime = -1  # 此变量作为login_in()函数是否登录成功的标志,若登陆成功,那么prime为用户下标
        user_name = input("请输入用户名:")
        user_password = input("请输入密码:")
        prime, grade_flag = login_in(user_name, user_password, prime)
        if prime == -1:  # 说明用户名和密码不正确,要重新输入
            continue
        else:
            print("当前选择为%s出题\n准备生成%s数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):"%(grade[prime], grade[prime]))
            while 1:
                flag = 0  #作为number_or_string函数的参数,判断输入的是数字还是切换年级指令
                '''输入的是数字,返回值result为输入的数字、flag(0,说明是数字)、-1(说明不是切换年级指令)
                输入的是切换指令,返回值result是切换的年级、flag(1,说明是切换指令)、年级标志
                '''
                result, flag, grade_flag_temp = number_or_string(flag)
                if grade_flag_temp!=-1:   # 说明输入的不是数字,而是切换年级指令,此时此变量代表的是年级标志
                    grade_flag=grade_flag_temp
                if flag == 0:  # flag=0说明输入的是数字(题目数量或-1),跳出此循环
                    break
                else:   # 否则说明输入的是切换年级的指令,返回值result是年级(小学,初中或高中)
                    grade[prime]=result   # 切换当前用户的年级
                    continue      # 并继续循环,继续输入并判断内容
            if 10 <= int(result) <= 30:   # 如果题目数量在范围内,则开始生成题目
                print("正在生成%d道%s数学题" % (int(result), grade[prime]))
                set_questions(int(result), grade_flag, user_name, grade[prime])
                print("题目生成成功,请在相应目录文件夹下查看")
            elif int(result) == -1:       # 如果是-1,则退出当前账号
                continue
            else:
                print("题目数量不符合要求!")
            #print(grade_flag)

if __name__ == "__main__":
    main()

你可能感兴趣的:(python)