python评测器,初步实现
看到TB神牛写了个sh评测脚本,我就也写了一个python评测脚本。
通过这个脚本学了不少python知识,比如子进程控制、文件比对、异常处理。
这还只是个简单的实现,不能卡时、卡内存。不过我对卡时已经有想法了,不久就会写出来。
使用方法:
judge.py 评测数据目录 程序源码或程序
eg: judge.py ~/media/pet/ ~/src/pet.cpp
程序必须采用屏幕输入输出。
输入数据文件扩展名.in,输出扩展名.out或.ans。根据文件名,相同文件名的一组作为同一组数据。文件比对忽略空行。
支持的源代码(按扩展名判定):
C/C++ (c,cpp,cxx),Pascal (pas),Python (py),Ruby (rb),Shell脚本(sh),普通可执行文件(exe)
#!/usr/bin/python2
# -*- coding: utf-8 -*-
def PrintError(s, flexit=0 ):
print ' (!!)',s
if flexit:
print ' (!!) 发生严重错误,评测中止'
exit(1)
def gocompile (s):
""" 编译源代码 s:源代码文件"""
import subprocess
sourceext={
".c" : ["gcc", "-o", "/tmp/temp.exe", "-O3"],
".cpp" : ["g++", "-o", "/tmp/temp.exe", "-O3"],
".cxx" : ["g++", "-o", "/tmp/temp.exe", "-O3"],
".pas" : ["fpc", "-o", "/tmp/temp.exe", "-O3"]
}
compilecmd = sourceext.get(os.path.splitext(s)[1])
if compilecmd!=None:
compilecmd.append(s)
try:
proc = subprocess.Popen(compilecmd, stdin = subprocess.PIPE, \
stdout = subprocess.PIPE, stderr = subprocess.PIPE, \
shell = False)
except OSError:
PrintError(compilecmd[0]+" 编译器未找到", 1)
ret = proc.wait()
if ret==0:
return "/tmp/temp.exe"
else:
PrintError("编译错误,请检查源代码", 1)
else:
return s
def gorun (s, fin):
""" 运行程序 s:可执行文件 fin:输入文件"""
exeext={
".exe" : [],
".sh" : ["sh"],
".py" : ["python2"],
".rb" : ["ruby"]
}
import subprocess
execmd = exeext.get(os.path.splitext(s)[1])
if execmd!=None:
execmd.append(s)
try:
proc = subprocess.Popen(execmd, stdin = subprocess.PIPE, \
stdout = subprocess.PIPE, stderr = subprocess.PIPE, \
shell = False)
except OSError:
PrintError("未发现可执行文件", 1)
try:
proc.stdin.write(file(fin, "r").read())
except IOError:
PrintError("运行时错误: IOError")
ret = proc.wait()
if ret != 0:
PrintError("运行时错误 %d" % ret)
return proc.stdout
else:
PrintError("未发现可执行文件", 1)
def checkanswer(srcfile, basefile):
""" 检查答案 srcfile:比较的文件 fin:基准文件"""
import difflib
s = difflib.SequenceMatcher( None ,
filter(lambda x: not x in " \n\t", srcfile.read().split("\n")),
filter(lambda x: not x in " \n\t", basefile.read().split("\n")))
for tag, i1, i2, j1, j2 in s.get_opcodes():
if tag!='equal':
PrintError("输出对比不同")
print (tag, i1, i2, j1, j2)
return 1
return 0
def getfinout(s):
""" 获取输入输出文件列表 s:搜索目录"""
import glob
finlist = glob.glob( s + os.sep + '*.in')
flist = []
for fin in finlist:
basename = os.path.splitext(fin)[0]
if os.path.isfile( basename + '.out'):
flist.append((fin, os.path.splitext(fin)[0] + '.out'))
elif os.path.isfile( basename + '.ans'):
flist.append((fin, os.path.splitext(fin)[0] + '.ans'))
return flist
if __name__ == '__main__':
import sys, os
if len(sys.argv)<3 or \
(not os.path.isdir(sys.argv[1]) or not os.path.isfile(sys.argv[2])):
PrintError("参数格式错误", 1)
exefile = gocompile(sys.argv[2])
count = 0
score = 0
for fin, fout in getfinout(sys.argv[1]):
print "\n输入: %s\n输出: %s" % (fin, fout)
print " 运行程序..."
count+=1
if checkanswer(gorun(exefile, fin), open(fout, "r")) == 0:
print " < 正确 >"
score+=1
else:
print " < 错误 >"
print "\n\n评测结束!"
if count>0:
print "得分: %d (%d/%d)" % \
(int(float(score)/float(count)*100), score, count)