由成幻OnlineJudge学习如何做自己的Acm-Icpc在线评判系统-5.在线编译与测试系统代码粗解
由于近来很忙,加之此类文章少有人问津.所以少有时间写这一系列的文章的续篇
所以先把核心在线编译的代码发出来,细的部分大家可以查资料,或者留言问我^^
部分类库可以查我以前的文章
上篇:由成幻OnlineJudge学习如何做自己的Acm-Icpc在线评判系统-4.建立基本的网站类库
所以先把核心在线编译的代码发出来,细的部分大家可以查资料,或者留言问我^^
部分类库可以查我以前的文章
using System;
using System.Text;
using System.Data;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace ChswordOJ {
/// <summary>
/// DoFile 进行在线编译与测试
/// Builder:邹健 2007 4 28
/// V1.0 2007 6 12
/// </summary>
public class DoFile {
StringBuilder _output = new StringBuilder();
StringBuilder _TestOutput = new StringBuilder();
StringBuilder _TestFile_Output = new StringBuilder();
String _Test;
String _CompilerName;
StringBuilder _CompilerText = new StringBuilder();
private Int64 _Memory, _myMemory;
private Int32 _Time;//, _myTime;
private Option.AnswerStatus _TestResult;
private String _ExePath;
private String _CodePath;
private String _TextPath;
private String _TestPath;
private Int64 _AnswerId;
private String _Sign;
private Boolean _TestEnd;
Process _process;
/// <summary>
/// 构造函数,对DoFile类进行初始化
/// </summary>
public DoFile() {
_Memory = 23768;
_Time = 3;
}
/// <summary>
/// 进行编译及测试代码。
/// </summary>
/// <param name="code">要编译的代码。</param>
/// <param name="UserName">编译的用户名。</param>
/// <param name="PassWord">用户密码。</param>
/// <param name="QNum">代码的问题ID。</param>
/// <param name="CompilerName">编译器。</param>
public void GetResult(String code, String UserName, String PassWord, String QNum, String CompilerName) {
Option cp = new Option();
DataSet ds = cp.GetLimit(QNum);
User u = new User(UserName, CompilerName.Split(',')[0], CompilerName.Split(',')[1]);
_Memory = int.Parse(ds.Tables[0].Rows[0][0].ToString());//从数据库读取当前的程序 要求的Memory
_Time = int.Parse(ds.Tables[0].Rows[0][1].ToString());//从数据库读取当前的程序 要求的Time limit
_Test = ds.Tables[0].Rows[0]["test"].ToString();//读取测试文件
_CompilerName = CompilerName;//编译器名称
_Sign = u.Sign;
_CodePath = u.CodePath;//源代码文件保存的路径
_TextPath = u.TextPath;//生成文本文件保存的路径
_ExePath = u.ExePath;//EXE文件保存的路径
_myMemory = 0;//对我当前耗费的内存进行初始化
_TestPath = u.GetTestPath(QNum);//得到当前测试路径
_AnswerId = cp.AddAnswer(UserName, Int64.Parse(QNum), CompilerName);//将当前数据作为一条回答存入数据库(创建一条数据库记录)
Code cc = new Code(CompilerName, code);//创建Code实例
if (cc.Check()) {//调用配置文件中的正则表达式,测试代码中有无危险代码,有,则此回答为危险代码
SaveTextFile(u.CodePath, code);//将源代码保存为相应格式文件
_CompilerText.AppendLine(u.CompilerString);//
DoCmd(u.CompilerString);//用u生成编译字符串,执行编译操作
IsTempExists(u.TextPath, u.CompilerString);//是否编译完成
if (0 == _AnswerId)
return;
cp.SetAnswerStatus(_AnswerId, Option.AnswerStatus.编译中);
//编译完成
if (IsCompilerSucess(u.TextPath, u.CodePath, u.ExePath, 0)) {//编译产生EXE则编译成功
//编译成功
cp.SetAnswerStatus(_AnswerId, Option.AnswerStatus.测试中);
ThreadStart thr_start_func = new ThreadStart(Test);//异步调用Test进行测试
Thread fThread = new Thread(thr_start_func);
fThread.Name = "Test";
fThread.Start();
Thread.Sleep(_Time * 2000);//初始化最大超时时间(规定时间的2倍)
if (Option.AnswerStatus.测试通过 == _TestResult) {
//cp.SetAnswerStatus(_AnswerId, Compiler.AnswerStatus.测试通过);
}
else {
if (_TestResult == 0) {
try {
//对进程查看是否结束,未结束则进行操作
if (!_process.HasExited) {
if (!_process.CloseMainWindow())
_process.Kill();
if (_TestFile_Output.ToString().StartsWith(_TestOutput.ToString()))
_TestResult = Option.AnswerStatus.超时;
else
_TestResult = Option.AnswerStatus.测试失败;
}
}
catch {
_TestResult = Option.AnswerStatus.测试失败;
}
}
if (fThread.IsAlive) fThread.Abort();//最终的强制结束进程
}
}
else {
_TestResult = Option.AnswerStatus.编译失败;
cp.SetAnswerText(_AnswerId, ReplaceFileName(_CompilerText.ToString()));
}
try {
if (_TestResult == Option.AnswerStatus.测试中 || _TestResult == Option.AnswerStatus.测试失败) {
Process[] p = Process.GetProcessesByName(_Sign);
if (p.Length > 0) {
if (!p[0].HasExited) {
if (p[0].Responding) {
p[0].CloseMainWindow();
p[0].Kill();
}
else {
p[0].Kill();
}
}
if (_TestFile_Output.ToString().StartsWith(_TestOutput.ToString()))
_TestResult = Option.AnswerStatus.超时;
else
_TestResult = Option.AnswerStatus.测试失败;
}
}
}
catch { }
}
else {
_TestResult = Option.AnswerStatus.危险代码;
}
cp.SetAnswerStatus(_AnswerId, _TestResult);//写入测试结果
cp.SaveAnswerCode(_AnswerId, code);//写入提交的源代码
/*try {
if (_TestResult == Option.AnswerStatus.测试通过) {
using (StreamReader sr = File.OpenText(_CodePath)) {
cp.SaveAnswerCode(_AnswerId, sr.ReadToEnd());
}
}
}
catch { }*/
DeleteTempFile();//删除临时文件
}
private void DeleteTempFile() {//删除临时文件
Byte b = 0;
try {
Thread.Sleep(1000);//每一秒试删除一次
if (File.Exists(_ExePath)) File.Delete(_ExePath); else b++;
if (File.Exists(_CodePath)) File.Delete(_CodePath); else b++;
if (File.Exists(_TextPath)) File.Delete(_TextPath); else b++;
}
catch { }
if (b != 3) DeleteTempFile();//递归调用
return;
}
private bool IsCompilerSucess(string TextPath, string CppPath, String ExePath, int count) {
try {//看是否编译成功的函数
using (StreamReader sr = new StreamReader(TextPath, Encoding.Default)) {
String input;
bool f = true;
while ((input = sr.ReadLine()) != null) {
_CompilerText.AppendLine(input);
if (input.Contains(": error:")) {
_output.AppendLine(input);
}
else {
if (f && !input.StartsWith(CppPath))
_output.AppendLine(input);
f = false;
}
}
sr.Close();
if (File.Exists(ExePath)) {
return true;
}
else {
_output.Insert(0, "编译出错:\r\n");
return false;
}
}
}
catch {
}
Thread.Sleep(1000);
if (count >= 300) {
_CompilerText.Insert(0, "服务器响应超时或您选择的编译器不正确");
return false;
}
return IsCompilerSucess(TextPath, CppPath, ExePath, count + 1);
}
private string ReplaceFileName(string input) {
input = System.Text.RegularExpressions.Regex.Replace(input, @"([A-Z]:[\\|\/][^:\*\?<>\|]+\.(txt|cs|cpp|c|vb|jsl|js|exe))|(\\{2}[^/:\*\?<>\|]+\.(txt|cs|cpp|c|vb|jsl|js|exe))", ">", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
return input;
}
private void Process_Exited(object sender, EventArgs e) {//测试程序是否超时的判断
if (_process.TotalProcessorTime.Milliseconds > _Time*1000) {
if (_TestFile_Output.ToString().StartsWith(_TestOutput.ToString()))
_TestResult = Option.AnswerStatus.超时;
else
_TestResult = Option.AnswerStatus.测试失败;
}
}
private void Test() {//进行测试
string ExePath = _ExePath;
string TestPath = _TestPath;
String input;
bool f = true;
_TestEnd = false;
_process = new Process();
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.UseShellExecute = false;
_process.StartInfo.CreateNoWindow = true;
_process.StartInfo.RedirectStandardInput = true;
_process.StartInfo.RedirectStandardOutput = true;
_process.StartInfo.RedirectStandardError = true;
_process.StartInfo.FileName = ExePath;
_process.Exited += new EventHandler(Process_Exited);
_process.OutputDataReceived += new DataReceivedEventHandler(TestOutputHandler);
_process.Start();
_process.BeginOutputReadLine();
_myMemory = (_process.PeakWorkingSet64 >> 10);//获取内存大小
StreamWriter sortStreamWriter = _process.StandardInput;//设置异步读取流
try {
using (StringReader sr = new StringReader(_Test)) {
while (((input = sr.ReadLine()) != null)) {
if (input.StartsWith("EOF"))//C/C++程序的结束(CTRL+Z)方式
if (_CompilerName.ToLower() == "c" || _CompilerName.ToLower() == "cpp")
sortStreamWriter.WriteLine(((char)26).ToString());
else//.net程序的结束(CTRL+Z)方式
_process.CloseMainWindow();
if (input.StartsWith(">"))//测试的写操作
sortStreamWriter.WriteLine(input.Substring(1));
if (input.StartsWith("<"))//测试的读操作
_TestFile_Output.AppendLine(input.Substring(1));
}
}
sortStreamWriter.Close();
_process.WaitForExit();
_TestEnd = true;
}
catch {
_TestEnd = true;
}
if (_TestOutput.ToString() == _TestFile_Output.ToString())
f = true;
else
f = false;
if (_TestEnd && f)
_TestResult = Option.AnswerStatus.测试通过;
else {
}
if (_TestEnd && (_myMemory > _Memory))//内存消耗太大
{
_TestResult = Option.AnswerStatus.内存超量;
}
_process.Close();
_process.Dispose();
if (f && _TestEnd) {
_TestResult = Option.AnswerStatus.测试通过;
}
return;
}
void SaveTextFile(string Path, string text) {
using (StreamWriter sw = new StreamWriter(Path)) { sw.Write(text); }
}
void DoCmd(string cmd) {//执行一条CMD命令的函数
Process process = new Process();
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.FileName = "cmd.exe";
process.Start();
process.StandardInput.WriteLine(cmd);
process.StandardInput.WriteLine("\r\nexit");
process.Close();
process.Dispose();
}
bool IsTempExists(String TextPath, String CompilerString) {
int i = 0;
while (!File.Exists(TextPath)) {
Thread.Sleep(1000);
i++;
if (i > 178) return false;
if (i % 30 == 0 && i > 10) {
DoCmd(CompilerString);
}
}
return true;
}
private void TestOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine) {
int numOutputLines = 0;
if (!String.IsNullOrEmpty(outLine.Data)) {
numOutputLines++;
//Environment.NewLine +"[" + numOutputLines.ToString() + "] - " +
_TestOutput.AppendLine(outLine.Data);
}
}
}
}
using System.Text;
using System.Data;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace ChswordOJ {
/// <summary>
/// DoFile 进行在线编译与测试
/// Builder:邹健 2007 4 28
/// V1.0 2007 6 12
/// </summary>
public class DoFile {
StringBuilder _output = new StringBuilder();
StringBuilder _TestOutput = new StringBuilder();
StringBuilder _TestFile_Output = new StringBuilder();
String _Test;
String _CompilerName;
StringBuilder _CompilerText = new StringBuilder();
private Int64 _Memory, _myMemory;
private Int32 _Time;//, _myTime;
private Option.AnswerStatus _TestResult;
private String _ExePath;
private String _CodePath;
private String _TextPath;
private String _TestPath;
private Int64 _AnswerId;
private String _Sign;
private Boolean _TestEnd;
Process _process;
/// <summary>
/// 构造函数,对DoFile类进行初始化
/// </summary>
public DoFile() {
_Memory = 23768;
_Time = 3;
}
/// <summary>
/// 进行编译及测试代码。
/// </summary>
/// <param name="code">要编译的代码。</param>
/// <param name="UserName">编译的用户名。</param>
/// <param name="PassWord">用户密码。</param>
/// <param name="QNum">代码的问题ID。</param>
/// <param name="CompilerName">编译器。</param>
public void GetResult(String code, String UserName, String PassWord, String QNum, String CompilerName) {
Option cp = new Option();
DataSet ds = cp.GetLimit(QNum);
User u = new User(UserName, CompilerName.Split(',')[0], CompilerName.Split(',')[1]);
_Memory = int.Parse(ds.Tables[0].Rows[0][0].ToString());//从数据库读取当前的程序 要求的Memory
_Time = int.Parse(ds.Tables[0].Rows[0][1].ToString());//从数据库读取当前的程序 要求的Time limit
_Test = ds.Tables[0].Rows[0]["test"].ToString();//读取测试文件
_CompilerName = CompilerName;//编译器名称
_Sign = u.Sign;
_CodePath = u.CodePath;//源代码文件保存的路径
_TextPath = u.TextPath;//生成文本文件保存的路径
_ExePath = u.ExePath;//EXE文件保存的路径
_myMemory = 0;//对我当前耗费的内存进行初始化
_TestPath = u.GetTestPath(QNum);//得到当前测试路径
_AnswerId = cp.AddAnswer(UserName, Int64.Parse(QNum), CompilerName);//将当前数据作为一条回答存入数据库(创建一条数据库记录)
Code cc = new Code(CompilerName, code);//创建Code实例
if (cc.Check()) {//调用配置文件中的正则表达式,测试代码中有无危险代码,有,则此回答为危险代码
SaveTextFile(u.CodePath, code);//将源代码保存为相应格式文件
_CompilerText.AppendLine(u.CompilerString);//
DoCmd(u.CompilerString);//用u生成编译字符串,执行编译操作
IsTempExists(u.TextPath, u.CompilerString);//是否编译完成
if (0 == _AnswerId)
return;
cp.SetAnswerStatus(_AnswerId, Option.AnswerStatus.编译中);
//编译完成
if (IsCompilerSucess(u.TextPath, u.CodePath, u.ExePath, 0)) {//编译产生EXE则编译成功
//编译成功
cp.SetAnswerStatus(_AnswerId, Option.AnswerStatus.测试中);
ThreadStart thr_start_func = new ThreadStart(Test);//异步调用Test进行测试
Thread fThread = new Thread(thr_start_func);
fThread.Name = "Test";
fThread.Start();
Thread.Sleep(_Time * 2000);//初始化最大超时时间(规定时间的2倍)
if (Option.AnswerStatus.测试通过 == _TestResult) {
//cp.SetAnswerStatus(_AnswerId, Compiler.AnswerStatus.测试通过);
}
else {
if (_TestResult == 0) {
try {
//对进程查看是否结束,未结束则进行操作
if (!_process.HasExited) {
if (!_process.CloseMainWindow())
_process.Kill();
if (_TestFile_Output.ToString().StartsWith(_TestOutput.ToString()))
_TestResult = Option.AnswerStatus.超时;
else
_TestResult = Option.AnswerStatus.测试失败;
}
}
catch {
_TestResult = Option.AnswerStatus.测试失败;
}
}
if (fThread.IsAlive) fThread.Abort();//最终的强制结束进程
}
}
else {
_TestResult = Option.AnswerStatus.编译失败;
cp.SetAnswerText(_AnswerId, ReplaceFileName(_CompilerText.ToString()));
}
try {
if (_TestResult == Option.AnswerStatus.测试中 || _TestResult == Option.AnswerStatus.测试失败) {
Process[] p = Process.GetProcessesByName(_Sign);
if (p.Length > 0) {
if (!p[0].HasExited) {
if (p[0].Responding) {
p[0].CloseMainWindow();
p[0].Kill();
}
else {
p[0].Kill();
}
}
if (_TestFile_Output.ToString().StartsWith(_TestOutput.ToString()))
_TestResult = Option.AnswerStatus.超时;
else
_TestResult = Option.AnswerStatus.测试失败;
}
}
}
catch { }
}
else {
_TestResult = Option.AnswerStatus.危险代码;
}
cp.SetAnswerStatus(_AnswerId, _TestResult);//写入测试结果
cp.SaveAnswerCode(_AnswerId, code);//写入提交的源代码
/*try {
if (_TestResult == Option.AnswerStatus.测试通过) {
using (StreamReader sr = File.OpenText(_CodePath)) {
cp.SaveAnswerCode(_AnswerId, sr.ReadToEnd());
}
}
}
catch { }*/
DeleteTempFile();//删除临时文件
}
private void DeleteTempFile() {//删除临时文件
Byte b = 0;
try {
Thread.Sleep(1000);//每一秒试删除一次
if (File.Exists(_ExePath)) File.Delete(_ExePath); else b++;
if (File.Exists(_CodePath)) File.Delete(_CodePath); else b++;
if (File.Exists(_TextPath)) File.Delete(_TextPath); else b++;
}
catch { }
if (b != 3) DeleteTempFile();//递归调用
return;
}
private bool IsCompilerSucess(string TextPath, string CppPath, String ExePath, int count) {
try {//看是否编译成功的函数
using (StreamReader sr = new StreamReader(TextPath, Encoding.Default)) {
String input;
bool f = true;
while ((input = sr.ReadLine()) != null) {
_CompilerText.AppendLine(input);
if (input.Contains(": error:")) {
_output.AppendLine(input);
}
else {
if (f && !input.StartsWith(CppPath))
_output.AppendLine(input);
f = false;
}
}
sr.Close();
if (File.Exists(ExePath)) {
return true;
}
else {
_output.Insert(0, "编译出错:\r\n");
return false;
}
}
}
catch {
}
Thread.Sleep(1000);
if (count >= 300) {
_CompilerText.Insert(0, "服务器响应超时或您选择的编译器不正确");
return false;
}
return IsCompilerSucess(TextPath, CppPath, ExePath, count + 1);
}
private string ReplaceFileName(string input) {
input = System.Text.RegularExpressions.Regex.Replace(input, @"([A-Z]:[\\|\/][^:\*\?<>\|]+\.(txt|cs|cpp|c|vb|jsl|js|exe))|(\\{2}[^/:\*\?<>\|]+\.(txt|cs|cpp|c|vb|jsl|js|exe))", ">", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
return input;
}
private void Process_Exited(object sender, EventArgs e) {//测试程序是否超时的判断
if (_process.TotalProcessorTime.Milliseconds > _Time*1000) {
if (_TestFile_Output.ToString().StartsWith(_TestOutput.ToString()))
_TestResult = Option.AnswerStatus.超时;
else
_TestResult = Option.AnswerStatus.测试失败;
}
}
private void Test() {//进行测试
string ExePath = _ExePath;
string TestPath = _TestPath;
String input;
bool f = true;
_TestEnd = false;
_process = new Process();
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.UseShellExecute = false;
_process.StartInfo.CreateNoWindow = true;
_process.StartInfo.RedirectStandardInput = true;
_process.StartInfo.RedirectStandardOutput = true;
_process.StartInfo.RedirectStandardError = true;
_process.StartInfo.FileName = ExePath;
_process.Exited += new EventHandler(Process_Exited);
_process.OutputDataReceived += new DataReceivedEventHandler(TestOutputHandler);
_process.Start();
_process.BeginOutputReadLine();
_myMemory = (_process.PeakWorkingSet64 >> 10);//获取内存大小
StreamWriter sortStreamWriter = _process.StandardInput;//设置异步读取流
try {
using (StringReader sr = new StringReader(_Test)) {
while (((input = sr.ReadLine()) != null)) {
if (input.StartsWith("EOF"))//C/C++程序的结束(CTRL+Z)方式
if (_CompilerName.ToLower() == "c" || _CompilerName.ToLower() == "cpp")
sortStreamWriter.WriteLine(((char)26).ToString());
else//.net程序的结束(CTRL+Z)方式
_process.CloseMainWindow();
if (input.StartsWith(">"))//测试的写操作
sortStreamWriter.WriteLine(input.Substring(1));
if (input.StartsWith("<"))//测试的读操作
_TestFile_Output.AppendLine(input.Substring(1));
}
}
sortStreamWriter.Close();
_process.WaitForExit();
_TestEnd = true;
}
catch {
_TestEnd = true;
}
if (_TestOutput.ToString() == _TestFile_Output.ToString())
f = true;
else
f = false;
if (_TestEnd && f)
_TestResult = Option.AnswerStatus.测试通过;
else {
}
if (_TestEnd && (_myMemory > _Memory))//内存消耗太大
{
_TestResult = Option.AnswerStatus.内存超量;
}
_process.Close();
_process.Dispose();
if (f && _TestEnd) {
_TestResult = Option.AnswerStatus.测试通过;
}
return;
}
void SaveTextFile(string Path, string text) {
using (StreamWriter sw = new StreamWriter(Path)) { sw.Write(text); }
}
void DoCmd(string cmd) {//执行一条CMD命令的函数
Process process = new Process();
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.FileName = "cmd.exe";
process.Start();
process.StandardInput.WriteLine(cmd);
process.StandardInput.WriteLine("\r\nexit");
process.Close();
process.Dispose();
}
bool IsTempExists(String TextPath, String CompilerString) {
int i = 0;
while (!File.Exists(TextPath)) {
Thread.Sleep(1000);
i++;
if (i > 178) return false;
if (i % 30 == 0 && i > 10) {
DoCmd(CompilerString);
}
}
return true;
}
private void TestOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine) {
int numOutputLines = 0;
if (!String.IsNullOrEmpty(outLine.Data)) {
numOutputLines++;
//Environment.NewLine +"[" + numOutputLines.ToString() + "] - " +
_TestOutput.AppendLine(outLine.Data);
}
}
}
}
上篇:由成幻OnlineJudge学习如何做自己的Acm-Icpc在线评判系统-4.建立基本的网站类库