用Python写个空课表生成器
开发背景
刚入大学的CYQ加入了我们学校的学生会,面对繁杂的工作,能“偷懒”就“偷懒”。这不,最近要举办几场活动,部门要安排人员值班,需要十几人的空课表。此时,CYQ自告奋勇,接下了制作任务,没想到,困难重重,效率低下,重复性劳动,小学加减法算得头皮发麻。聪明的CYQ终于在倒下前的那一刻,想起了自己的身份,在午夜时分,完成了这个小程序。
前提准备
- Python3.7
- 安装xlrd,xlwd第三方库
(建议用pip安装,官方原装速度感人,镜像速度贼快)自行百度,python国内镜像源 - 电脑
- 南昌航空大学官方总课表(这里仅仅是对我们学校的版本,可自行更改源代码,达到自己的需求)
- 脑子
需求分析
- 能够根据一份总的课程表(有课)生成一份空课表Excel表
- 并能根据开始周数和结束周数进行在线生成
- 操作要求简单,易操作
功能实现(较为简单)
- 生成函数
- 可视化图形界面(日后优化)
问题分析
- 如何实现对xls文件读取呢?
我们借用了第三方库xlrd,xlwd对于这两种函数的介绍,这里不细讲。
我们知道xlrd是用于读取.xls文件的,xlwd是写入.xls文件的,我们可以通过观察,r和w的区别记忆,read和write。
这里有几个关键的用法
- xrld部分
用于打开一个Excel文件
work_book = xlrd.open_workbook('filename.xls')
获取Excel文件的所有sheet,这里我们暂时不用,因为CYQ的课表只有一个sheet
work_sheets = work_book.sheet_names()
通过索引来访问一个表单
table = work_book.sheet_by_index(0)
获取到文档的行数
nrows = table.nrows
类推这是列数
ncols = table.ncols
通过索引来访问第row_index行的所有数据,以字符串形式返回
table.row_values(row_index)
- 如何实现对xls文件写入操作呢?
- xrwd部分
这是对写入文档的编码规定为ascii
workbook = xlwt.Workbook(encoding = 'ascii')
创建一个sheet,名称为sheet_1
里面的cell_overwrite_ok = True参数是为了防止重复操作,导致报错
sheet_1 = workbook.add_sheet('sheet_1',cell_overwrite_ok = True)
新建一个样式
style = xlwt.XFStyle()
字体样式
font = xlwt.Font()
字体类型为这个东东
font.name = 'Times New Roman'
实例化
font.bold = True
style.font = font
这是最重要的,把Contents写入到sheet_1中的row行col列,用你设置的style来写入(也可以不用style,上面的新建样式就可以省去)
sheet_1.write(row,col,"Contens",style)
- 数据的处理
首先,确定我们要的数据结构
用字典用得方便些
所以用一个字典来存储全部人的数据
all_the_data = {}##存储总的数据
首先获取本目录下的满足要求的文件
import os
files = [d for d in os.listdir('.')]
通过生成器来生成一个文件列表
之后通过一个大循环来获取所需的数据(根据我们课表的特征)
名字在第一行,可以通过正则来匹配
前三行为无效数据,最后一行为无效数据
剩下的为有效数据,通过正则来匹配
大循环开始
for i in files:
if (i[-4:]=='.xls' and i != 'Blank_Courses.xls'):
work_book = xlrd.open_workbook(i) #open file to read contens
work_sheets = work_book.sheet_names()
table = work_book.sheet_by_index(0) # by_name('')sheet()[x]
nrows = table.nrows
ncols = table.ncols
name = re.findall(' (.*?) ',table.cell(0,0).value)[0]
print(name+"OK!\n")
course = {}#用一个字典来存储一个循环里面一个人的信息
for i in range(nrows-4):#从上往下遍历表单内容
li = [] #用一个表存储临时数据
for j in table.row_values(i+3):
li.append(j)
course[i] = li
以上有个判断,从本目录下的所有xls文件,过滤生成文件,加入到程序的处理链条中
匹配一下名字,正则这样写,会更好一点,名字存在name里面
之后从上到下遍历初始数据
为了得到有效信息,我们继续处理
Course = {}
for index in range(len(course)):
li_a = [] #创建一个列表,储存每大节课的周
for i in range(len(course[index])):#从每大节课遍历
if(i==0): #跳过无效数据
continue
li = []#创建一个小列表,存储星期所对的课
data = re.findall(r'\n.*?\n.*?\n(.*?)\n.*?\n',course[index][i])
#正则匹配数据
for j in data:
i.append(j.replace('[周]','').split(',')) #存储到小列表中
li_a.append(li) #存储到大列表中
Course[index] = li_a #将列表存储到字典中,获得了一个完整的有课表
聪明的你如果对正则感兴趣的话,可以搜索相关资料。
上面我们又对数据进行了处理,将时间匹配到了
程序设计基础
杨老师()
9[周]
B203
我们学校的表单信息是这种格式,正则就按上面这样写
之后我们同理,将数据存储起来
for index in Course:
L = []
for i in range(len(Course[index])):
have = e_to_have(Course[index][i])#生成有课的所有时间点
ha_not = e_to_not(have,start,end)#生成无课的区间,和所有点
if(index not in all_the_data.keys()):
s = '\n'+name #这是
else:
s = all_the_data[index][i] + '\n' + name
#追加写入,不删除之前的我数据
temp = ' '#用来判断是不是全员无课的标记
for j in ha_not:
temp = temp+j+' '
if(temp==' '):L.append(' '+s)#标记在这里添加
else :L.append(s+temp)
all_the_data[index] = L #存入总的数据字典里
大循环到此结束
以上我们使用了两个函数
def e_to_have(li):
L = []
for i in li:#传入的i是一个列表,但只有一个元素,用i[0]表示它的值
if(len(i[0])<=2):#没有出现-连接(表示区间)时,小于2
L.append(i[0])
else:
sp = i[0].split('-')##进行切片
for i in range(int(sp[1])-int(sp[0])+1):
#循环加入单点到有课列表
L.append(str(int(sp[0])+i))
return L
转化为无课的区间或者时间点
def e_to_not(li,start,end):
L = []##生成无课单点表
for i in range(end-start+1):##循环次数,也表示总的课区间
if(str(i+start)not in li):
L.append(i+start)
#若此单点不在有课表内,就表示无课,加入其中
S = []##生成无课区间和单点
po = 0##初始一个区间头
for i in range(len(L)-1):
if(L[i]+1!=L[i+1]):
if(L[po]!=L[i]):S.append(str(L[po])+'-'+str(L[i]))
else:S.append(str(L[po]))
po = i+1
else:
if(i==len(L)-2):
S.append(str(L[po])+'-'+str(L[i+1]))
#此算法要对最后一个要进行特判,才能加入连续区间
return S
里面的算法就不细讲了,思路重要
之后我们进行写入操做
将字典里面的数据依次写入到文件里面
#进行写入操作
workbook = xlwt.Workbook(encoding = 'ascii')
sheet_1 = workbook.add_sheet('NCHU_Blank_Courses',cell_overwrite_ok = True) #create sheet object
style = xlwt.XFStyle()
font = xlwt.Font()
font.name = 'Times New Roman'
font.bold = True
style.font = font #字体样式设置,可以跳过
for index in range(len(all_the_data)):
for i in range(len(all_the_data[index])):
sheet_1.col(i).width = 256*25
sheet_1.row(index).height = 256*25
if (all_the_data[index][i][:2] != ' '):
sheet_1.write(index,i,all_the_data[index][i],style)
else:
sheet_1.write(index,i,"全员无课",style)
到这里大概的框架就完成了,接下来就是漫长的迭代过程。或者是shi沉大海
不过我们也不能忘记了用户体验,配置文件README.docx
还是要写一下的,程序开头的初步使用操作也是要写的
程序执行过程中的状态,也是要写的,哪怕甩点什么数据出来也是可以的
麻雀虽小,五脏可还得俱全啊!
最后来甩几张图
最后完整代码
def e_to_have(li):
L = []
for i in li:##i是一个列表,但只有一个元素,用i[0]表示它的值
if(len(i[0])<=2):#没有出现-连接(表示区间)时,小于2
L.append(i[0])
else:
sp = i[0].split('-')##进行切片
for i in range(int(sp[1])-int(sp[0])+1):##循环加入单点到有课列表
L.append(str(int(sp[0])+i))
return L
def e_to_not(li,start,end):
L = []##生成无课单点表
for i in range(end-start+1):##循环次数,也表示总的课区间
if(str(i+start)not in li):
L.append(i+start)##若此单点不在有课表内,就表示无课,加入其中
S = []##生成无课区间和单点
po = 0##初始一个区间头
for i in range(len(L)-1):
if(L[i]+1!=L[i+1]):
if(L[po]!=L[i]):S.append(str(L[po])+'-'+str(L[i]))
else:S.append(str(L[po]))
po = i+1
else:
if(i==len(L)-2):
S.append(str(L[po])+'-'+str(L[i+1]))##此算法要对最后一个要进行特判,才能加入连续区间
return S
def main():
print("=========================\n使用前,阅读使用文档(README.docx)\n目前只可以使用南昌航空大学课程表\n请将成员课表(.xls文件)放在本程序目录下\n目录下不要放其他文件\n本程序上级目录尽量不要为中文名\nMADE FOR NCHU WITH PYTHON\n=========================")
try:
print("请输入课程周数内的合理数字\n")
start = int(input("请输入开始周数:\n"))
end = int(input("请输入结束周数:\n"))
if(start > 25 or start < 0 or end > 25 or end < 0 or start > end):
print("请输入课程合理的数字\n你的输入有误,程序自动退出")
input()
return
except:
print("请输入合理的数字\n你的输入有误,程序自动退出")
input()
return
import re,os
import xlrd #用来读取excel文件,不能修改数据
import xlwt #创建Excel文件并对其进行操作,但不能对已有的Excel文件进行修改
import xlutils #对已有的Excel文件进行修改
all_the_data = {}##存储总的数据
files = [d for d in os.listdir('.')]
for i in files:
if (i[-4:]=='.xls' and i != 'Blank_Courses.xls'):
####################################循环体
work_book = xlrd.open_workbook(i) #open file to read contens
work_sheets = work_book.sheet_names()
table = work_book.sheet_by_index(0) # by_name('')sheet()[x]
nrows = table.nrows
ncols = table.ncols
name = re.findall(' (.*?) ',table.cell(0,0).value)[0]
print(name+"OK!\n")
course = {}
for i in range(nrows-4):#从上往下遍历
li = [] #用一个表存储数据
for j in table.row_values(i+3):
li.append(j)
course[i] = li
Course = {}
for index in range(len(course)):
li_a = [] #创建一个列表,储存每大节课的周
for i in range(len(course[index])):#从每大节课遍历
if(i==0): #跳过无效数据
continue
li = []#创建一个小列表,存储星期所对的课
data = re.findall(r'\n.*?\n.*?\n(.*?)\n.*?\n',course[index][i])#正则匹配数据
for j in data:
li.append(j.replace('[周]','').split(',')) #存储到小列表中
li_a.append(li) #存储到大列表中
Course[index] = li_a #将列表存储到字典中,获得了一个完整的有课表
for index in Course:
L = []
for i in range(len(Course[index])):
have = e_to_have(Course[index][i])
ha_not = e_to_not(have,start,end)
if(index not in all_the_data.keys()):
s = '\n'+name
else:
s = all_the_data[index][i] + '\n' + name
temp = ' '
for j in ha_not:
temp = temp+j+' '
if(temp==' '):L.append(' '+s)
else :L.append(s+temp)
all_the_data[index] = L
##############################循环体
#进行写入操作
workbook = xlwt.Workbook(encoding = 'ascii')
sheet_1 = workbook.add_sheet('NCHU_Blank_Courses',cell_overwrite_ok = True) #create sheet object
style = xlwt.XFStyle()
font = xlwt.Font()
font.name = 'Times New Roman'
font.bold = True
style.font = font
for index in range(len(all_the_data)):
for i in range(len(all_the_data[index])):
sheet_1.col(i).width = 256*25
sheet_1.row(index).height = 256*25
if (all_the_data[index][i][:2] != ' '):
sheet_1.write(index,i,all_the_data[index][i],style)
else:
sheet_1.write(index,i,"全员无课",style)
#sheet_1.write(index,i,s,style)
#sheet_1.write(r,c,val,style) #write dates in the sheet whith style
workbook.save('Blank_Courses.xls')
print("=========================\nCongratulations!生成空课表成功!\n若程序出现问题可以互相交流QQ1614143338\n=========================")
input()
# print(Course)
if __name__ == '__main__':
main()
写的不是很好,由于写入文件需要在同一函数中,创建文件和保存文件,导致很难再拆分主函数里面的功能。那就为了代码的集中,区域模块化,干脆就把读取数据也写入到主函数中。上述的原因导致不能较好的模块化程序,引发一些程序的漏掉和不稳定性,望大佬们指正。也可以一起交流学习。