前面介绍了考试系统学生端的功能:学生在线答题,提交答案,标注答案的状态为IsCompiling。
c#-asp net -C语言在线考试系统-考生端
https://blog.csdn.net/weixin_43917370/article/details/107790653
本篇介绍后台如何判题。
1、后台进程不断扫描solution表,取出答案状态为IsCompiling的那些
2、如果有编译错误,提示“不能生成exe”,如果能生成exe文件,转向第3步
3、对准备好的input数据,作为exe文件的输入,输出结果存入output文件
4、对比output文件和标准答案,如果完全相同,则accept,否则是答案错误,需要继续修改。
参考代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using System.Configuration;
using System.Data.SqlClient;
using System.IO;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
while (true)
{
string userid = "11";
string username = "11";
String connetStr = ConfigurationManager.ConnectionStrings["ConsoleApplication4.Properties.Settings.ExamConnectionString"].ConnectionString.ToString();
ExamDataClassesDataContext lqDB = new ExamDataClassesDataContext(connetStr);
var result = from r in lqDB.cmdb_solution
where r.CompileInfor=="IsCompiling"
select r;
//where r.userid == userid && r.username == username
if (result.Count() > 0)
{
//textBox1.Text = result.Count().ToString() + "\r\n";
foreach (cmdb_solution row in result)
{
//textBox1.Text = row.username + " " + row.userid + " " + row.DB_prob_id + "\r\n";
Judge(row);
}
}
//return;
}
}
static void Judge(cmdb_solution row)
{
string stu_ans = row.stu_ans;
string userid = row.userid, DB_prob_id = row.DB_prob_id;
Dictionary<string, string> res;
res = compile_code_function(stu_ans, userid, DB_prob_id); //源代码
Console.WriteLine("{0} {1} {2}", userid, DB_prob_id, res["output"]);
string CompileInfor2 = res["output"];
String connetStr = ConfigurationManager.ConnectionStrings["ConsoleApplication4.Properties.Settings.ExamConnectionString"].ConnectionString.ToString();
ExamDataClassesDataContext lqDB = new ExamDataClassesDataContext(connetStr);
var result = from r in lqDB.cmdb_solution
where r.userid == userid && r.DB_prob_id == DB_prob_id
select r;
//Console.WriteLine(CompileInfor2.IndexOf("AcceptAccepted"));
if (CompileInfor2.IndexOf("AcceptedAccepted") >-1)
{
foreach (cmdb_solution row2 in result)
{
row2.stu_score = row2.pro_score;
row2.CompileInfor = CompileInfor2;
break;
}
}
else
{
foreach (cmdb_solution row2 in result)
{
row2.stu_score = 0;
row2.CompileInfor = CompileInfor2;
break;
}
}
lqDB.SubmitChanges();
}
//根目录
static string rootPath = Directory.GetCurrentDirectory();
//测试目录为根目录下的:000tmp
static string tmp_dir_path = rootPath + "\\000tmp\\";
//下面是编译程序的全路径,相对目录
//exec_cmd = r'MinGW64/bin/x86_64-w64-mingw32-gcc.exe'
//exec_cmd = r'MinGW64/bin/x86_64-w64-mingw32-g++.exe'
static string exec_cmd = rootPath + "\\MinGW64\\bin\\x86_64-w64-mingw32-g++.exe";
static Dictionary<string, string> compile_code_function(string stu_ans, string userid, string DB_prob_id)
{
Dictionary<string, string> res = new Dictionary<string, string>();
res = zztPreCompile(stu_ans);
//textBox1.Text += res["output"] + "\r\n";
if (res["code"] == "0")//预编译,代码中有明显问题,例如使用宏
return res;
res = zztCompile(stu_ans, userid, DB_prob_id);
//textBox1.Text += res["output"] + "\r\n";
if (res["code"] == "0")//编译错误
return res;
//return res;
// 编译成功,也有可能不能生成exe
string exefile = tmp_dir_path + userid + "_" + DB_prob_id + "a.exe";
if (File.Exists(exefile))
{//文件存在,运行,用数据测试
res = zztRun(userid, DB_prob_id);//
return res;
}
else
{
res["code"] = "0";
res["output"] = "生成exe失败!!";
return res;
}
}
static Dictionary<string, string> zztPreCompile(string stu_ans)
{
Dictionary<string, string> res = new Dictionary<string, string>();
if (stu_ans == null)
{
res["code"] = "0";
res["output"] = "缺main函数";
return res;
}
if (stu_ans.IndexOf("main") < 0)
{
res["code"] = "0";
res["output"] = "缺main函数";
return res;
}
if (stu_ans.IndexOf("define") >= 0)
{
res["code"] = "0";
res["output"] = "请不要使用define语句";
return res;
}
if (stu_ans.IndexOf("fork") >= 0)
{
res["code"] = "0";
res["output"] = "恶意代码错误";
return res;
}
if (stu_ans.IndexOf("system") >= 0)
{
res["code"] = "0";
res["output"] = "恶意代码错误";
return res;
}
if (stu_ans.IndexOf("shut") >= 0)
{
res["code"] = "0";
res["output"] = "恶意代码错误";
return res;
}
res["code"] = "1";
res["output"] = "预编译通过!";
return res;
}
static Dictionary<string, string> zztCompile(string stu_ans, string userid, string pb_id)
{//编译函数
Dictionary<string, string> res = new Dictionary<string, string>();
string c_file_name = tmp_dir_path + userid + "_" + pb_id + ".c";
try
{//保存源文件
StreamWriter sw = new StreamWriter(c_file_name);
sw.Write(stu_ans);
sw.Close();
}
catch (IOException ex)
{
res["code"] = "0";
res["output"] = "Create Code File Failing!";
Console.WriteLine(ex.Message);
return res;
}
string exe_file_name = tmp_dir_path + userid + "_" + pb_id + "a";
string cmdStr = exec_cmd + " " + c_file_name + " -o " + exe_file_name; //##'-o' 一定得是小写
Process p = new Process();
//设定程序名
p.StartInfo.FileName = "cmd.exe";
//关闭Shell的使用
p.StartInfo.UseShellExecute = false;
//重定向标准输入
p.StartInfo.RedirectStandardInput = true;
//重定向标准输出
p.StartInfo.RedirectStandardOutput = true;
//重定向错误输出
p.StartInfo.RedirectStandardError = true;
//设置不显示窗口
p.StartInfo.CreateNoWindow = true;
string strPath = cmdStr;
//textBox1.Text += strPath + "\n\r";
p.Start();
// 切换到test目录下
//p.StandardInput.WriteLine("cd test");
//执行gcc命令
p.StandardInput.WriteLine(strPath);
// 等待编译完成
Thread.Sleep(500);
p.StandardInput.WriteLine("exit"); // 退出
//string str = p.StandardOutput.ReadToEnd(); // cmd显示的字符串,放入str中
string str = p.StandardError.ReadToEnd();//compile error
//textBox1.Text += "compile result:" + str + "\r\n";
p.Close();
p.Dispose();
File.Delete(c_file_name);//删除生成的源代码文件
//textBox1.Text += "exe:" + exe_file_name + "\r\n";//exe_file_name has no .exe
if (File.Exists(exe_file_name + ".exe"))
{
res["code"] = "1";
res["output"] = "编译成功";
return res;
}
else
{
res["code"] = "0";
//res["output"] = str+"未生成exe文件";
res["output"] = "编译错误,未生成exe文件";
return res;
}
}
//static string rootPath = Directory.GetCurrentDirectory();
//测试目录为根目录下的:000tmp
//string tmp_dir_path = rootPath + "\\000tmp\\";
static Dictionary<string, string> zztRun(string userid, string pb_id)
{//运行程序、测试数据
Dictionary<string, string> res = new Dictionary<string, string>();
// 文件重定向,用的管道
//需要把数据文件整理好
Process p = new Process();
//设定程序名
p.StartInfo.FileName = "cmd.exe";
//关闭Shell的使用
p.StartInfo.UseShellExecute = false;
//重定向标准输入
p.StartInfo.RedirectStandardInput = true;
//重定向标准输出
p.StartInfo.RedirectStandardOutput = true;
//重定向错误输出
p.StartInfo.RedirectStandardError = true;
//设置不显示窗口
//p.StartInfo.CreateNoWindow = true;
string data_dir_path = rootPath + "\\Data\\" + pb_id + @"\";
string input_file_name = data_dir_path + @"in.txt";
string answer_fime_name = data_dir_path + @"answer.txt";
string stu_answer_fime_name = rootPath + "\\000tmp\\" + userid + "_" + pb_id + @"useranswer.txt";
string exe_file_name = rootPath + "\\000tmp\\" + userid + "_" + pb_id + "a.exe";
string cmd_str = exe_file_name + " < " + input_file_name + " > " + stu_answer_fime_name;
p.Start();
p.StandardInput.WriteLine(cmd_str);
// 检查生成的myout.txt 和 指定的out.txt的数据对比
//p.StandardInput.WriteLine("FC myout.txt out.txt");
//p.StandardInput.WriteLine("exit"); // 退出
Thread.Sleep(500);
p.StandardInput.WriteLine("exit"); // 退出
//string str = p.StandardOutput.ReadToEnd(); // cmd显示的字符串,放入str中
//下面比较学生答案和标准答案
StreamReader fanswer = new StreamReader(answer_fime_name, Encoding.Default);
StreamReader fstu_answer = new StreamReader(stu_answer_fime_name, Encoding.Default);
string curr;
string user;
char[] ch = { ' ', '\r', '\n' };
curr = fanswer.ReadToEnd();
fanswer.Close();
user = fstu_answer.ReadToEnd();
fstu_answer.Close();
curr = curr.TrimEnd(ch);
user = user.TrimEnd(ch);
//textBox1.Text += curr + "\r\n";
//textBox1.Text += user + "\r\n";
if (user == curr)
{
res["code"] = "1";
//res["output"] = str+"未生成exe文件";
res["output"] = "AcceptedAccepted";
//textBox1.Text += res["output"] + "\r\n";
}
else
{
res["code"] = "0";
res["output"] = "请继续修改";
//textBox1.Text += res["output"] + "\r\n";
}
//textBox1.Text += "\r\n"+(p.ExitTime - p.StartTime).ToString() + "\r\n";
p.Close();
p.Dispose();
//删除.c,.exe,*.txt
File.Delete(exe_file_name);
File.Delete(stu_answer_fime_name);
return res;
}
}
}
1、ConsoleApplication4.exe是后台判题程序
2、MinGW64是C、C++编译器
3、Data对应每道题的input数据、标准答案
G0005是试题编号,对应文件夹内是input、answer。
4、
生成的exe文件、output文件存中oootmp目录下。