postWin-Cpp-Judger:一个轻量级C++代码测评工具
Win-Cpp-Judger:一个轻量级C++代码测评工具
简介
Win-Cpp-Judger 是一个基于Windowsx64平台的简易 (简陋)的C++代码测评工具 (其实就是一个 cpp ),主要用于批量测试多个C++程序在不同测试数据下的表现。它能够自动完成编译、运行、输出对比的全过程,并生成清晰的测评报告。
或许可以赏我一个 Star ?
(在此更推荐强大的你谷)
特点
|
高情商 |
低情商 |
| 自动化测评 |
一键完成编译、运行、对比全流程 |
unOnline Judge |
| 批量处理 |
支持多个代码文件和测试点的连续测试 |
for 循环 |
| 详细报告 |
支持多个代码文件和测试点的连续测试 |
cout |
| 简单配置 |
最小化的设置要求,开箱即用 |
只有 140 行的代码 |
环境要求
其实各位 OIer 都有罢
系统配置
- 操作系统:Windows 10 64位(不确保其他系统的兼容性)
- 编译器:GCC/G++(推荐MinGW或TDM-GCC)
- 磁盘空间:至少100MB可用空间
编译器安装
- 下载MinGW-w64或TDM-GCC
- 安装时选择添加至PATH环境变量
- 命令行输入
g++ --version以确保安装妥当
项目结构
1 2 3 4 5 6 7 8 9 10
| 项目根目录/ ├── judge.cpp(exe) # 测评代码(程序) ├── textpoint/ # 测试数据目录 │ ├── 1.in # 测试点1输入 │ ├── 1.out # 测试点1预期输出 │ ├── 2.in # 测试点2输入 │ └── 2.out # 测试点2预期输出 └── cpps/ # 待测评代码目录 ├── 1.cpp # 代码文件1 └── 2.cpp # 代码文件2
|
代码
建议看完后去看看后面的核心代码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
| #include <bits/stdc++.h> #include <Windows.h> #include <io.h> #include <direct.h> using namespace std; const string TESTPOINT_DIR = "textpoint"; const string CPP_DIR = "cpps"; const string TEMP_EXE = "temp_program.exe";
bool compileProgram(const string& cppFile) { string command = "g++ -std=c++11 -O2 -o " + TEMP_EXE + " " + cppFile; int result = system(command.c_str()); return result == 0; }
string runProgram(const string& inputFile) { string command = TEMP_EXE + " < " + inputFile + " > temp_output.txt"; system(command.c_str()); ifstream outputFile("temp_output.txt"); string output((istreambuf_iterator<char>(outputFile)), istreambuf_iterator<char>()); outputFile.close(); while (!output.empty() && (output.back() == '\n' || output.back() == '\r' || output.back() == ' ')) { output.pop_back(); } return output; }
string readFile(const string& filename) { ifstream file(filename); if (!file.is_open()) { return ""; } string content((istreambuf_iterator<char>(file)), istreambuf_iterator<char>()); file.close(); while (!content.empty() && (content.back() == '\n' || content.back() == '\r' || content.back() == ' ')) { content.pop_back(); } return content; }
void checkAndCreateDir(const string& dirName) { if (_access(dirName.c_str(), 0) == -1) { _mkdir(dirName.c_str()); } }
int main() { int n, m; cout << "请输入需测试代码数和测试点数: "; cin >> n >> m; checkAndCreateDir(TESTPOINT_DIR); checkAndCreateDir(CPP_DIR); for (int i = 1; i <= n; i++) { string cppFile = CPP_DIR + "/" + to_string(i) + ".cpp"; if (_access(cppFile.c_str(), 0) == -1) { cout << "代码文件 " << cppFile << " 不存在,跳过测试" << endl; continue; } cout << "正在编译 " << cppFile << "..." << endl; if (!compileProgram(cppFile)) { cout << "编译失败: " << cppFile << endl; continue; } vector<int> passedTests; vector<int> failedTests; int score = 0; for (int j = 1; j <= m; j++) { string inputFile = TESTPOINT_DIR + "/" + to_string(j) + ".in"; string expectedOutputFile = TESTPOINT_DIR + "/" + to_string(j) + ".out"; if (_access(inputFile.c_str(), 0) == -1 || _access(expectedOutputFile.c_str(), 0) == -1) { cout << "测试点 " << j << " 的文件不存在,跳过" << endl; continue; } string actualOutput = runProgram(inputFile); string expectedOutput = readFile(expectedOutputFile); if (actualOutput == expectedOutput) { passedTests.push_back(j); score += 10; } else { failedTests.push_back(j); } } cout << "\n目前测试代码名: " << cppFile << endl; cout << "通过测试点: "; if (passedTests.empty()) { cout << "无"; } else { for (int test : passedTests) { cout << test << " "; } } cout << endl; cout << "未通过测试点: "; if (failedTests.empty()) { cout << "无"; } else { for (int test : failedTests) { cout << test << " "; } } cout << endl; cout << "得分: " << score << "/" << m * 10 << "\n\n" << endl; remove(TEMP_EXE.c_str()); remove("temp_output.txt"); } return 0; }
|
核心代码解析
编译模块
1 2 3 4 5
| bool compileProgram(const string& cppFile) { string command = "g++ -std=c++11 -O2 -o " + TEMP_EXE + " " + cppFile; int result = system(command.c_str()); return result == 0; }
|
使用-O2优化等级确保运行效率,-std=c++11保证代码规范。
执行模块
1 2 3 4 5
| string runProgram(const string& inputFile) { string command = TEMP_EXE + " < " + inputFile + " > temp_output.txt"; system(command.c_str()); }
|
通过重定向实现输入输出,确保测试环境一致性。
结果对比
1 2 3 4 5 6
| if (actualOutput == expectedOutput) { passedTests.push_back(j); score += 10; } else { failedTests.push_back(j); }
|
严格对比实际输出和预期输出。
使用教程
第一步:准备测试数据
在textpoint目录下放置测试点文件,命名规则为:
1.in, 1.out
2.in, 2.out
- …(数字从1开始连续编号)
第二步:放置待测代码
在cpps目录下放置代码文件:
1.cpp
2.cpp
- …(数字从1开始连续编号)
第三步:运行测评
或直接按F11
第四步:输入参数
表示测试2个代码文件,使用3个测试点
测评报告示例
1 2 3 4 5 6 7 8 9 10 11 12 13
| 正在编译 cpps/1.cpp... 正在编译 cpps/2.cpp...
目前测试代码名: cpps/1.cpp 通过测试点: 1 2 3 未通过测试点: 无 得分: 30/30
目前测试代码名: cpps/2.cpp 通过测试点: 1 3 未通过测试点: 2 得分: 20/30
|
细节
文件处理
1 2 3 4 5
| void checkAndCreateDir(const string& dirName) { if (_access(dirName.c_str(), 0) == -1) { _mkdir(dirName.c_str()); } }
|
自动检查并创建所需目录,避免因目录缺失导致的运行错误。
输出标准化
1 2 3 4
| while (!output.empty() && (output.back() == '\n' || output.back() == '\r' || output.back() == ' ')) { output.pop_back(); }
|
去除末尾空白字符,确保对比的准确性 (效仿某些测评机) 。
常见问题排查
Q1: 编译失败
现象:显示”编译失败”
解决:
- 检查G++是否正确安装
- 确认代码文件语法正确
Q2: 测试点找不到
现象:显示”测试点X的文件不存在”
解决:
- 检查文件命名是否符合规则
- 确认文件放置在正确目录
Q3: 输出对比错误
现象:实际输出与预期输出不一致
解决:
- 检查文件编码(推荐UTF-8)
- 统一换行符格式(推荐LF)
扩展
你以为我会帮你拓展? (还真能)
但由于大家各自需要,不在源码中统一添加。各位大佬可以试着加些拓展,下为本蒟蒻示例。
添加时间限制
1 2 3 4 5
| DWORD waitResult = WaitForSingleObject(pi.hProcess, timeout); if (waitResult == WAIT_TIMEOUT) { TerminateProcess(pi.hProcess, 1); }
|
总结
Win-Cpp-Judger虽然功能简单 (简陋) ,但完整实现了自动化测评的基本需求。
但实质上对于大佬们而言只是一个简陋的小玩具,但是对于编程竞赛训练和算法学习来说,这是一个简单实用的工具。希望它能够帮助大家提高代码测试的效率!
(再次在此更推荐强大的你谷)
相关资源:
本文采用CC BY-NC-SA 4.0协议授权,转载请注明出处