【修改】 C语言使用TCC进行编译,无需配置环境变量

This commit is contained in:
dlaren
2025-08-21 13:17:47 +08:00
parent e67fb27f3f
commit fea4fe6192
2 changed files with 70 additions and 41 deletions

View File

@@ -5,12 +5,12 @@ import com.example.exam.exam.dal.ExamQuestionAnswer;
import com.example.exam.exam.dal.ExamQuestionKeyword;
import com.example.exam.exam.utils.HtmlAppender;
import com.example.exam.exam.utils.c.JudgementCUtils;
import com.example.exam.exam.utils.c.LogFileUtils;
import org.springframework.stereotype.Service;
import com.example.exam.exam.dal.SourceAndText;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -123,7 +123,9 @@ public class JudgementServiceImpl implements JudgementService
// 每个选项分值 = 总分 / 总权重
true_number += 1;
key_score += one_keyword_score * Integer.parseInt((String) item.get("score_rate"));
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 关键字比对:" + item.get("text") + " -- 正确 -- 得分:" + one_keyword_score * Integer.parseInt((String) item.get("score_rate")));
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 关键字比对:" + item.get("text") + " -- 正确 -- 得分:" +
new BigDecimal(one_keyword_score * Integer.parseInt((String) item.get("score_rate")))
.setScale(2, RoundingMode.HALF_UP));
} else {
// 关键字不正确
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ 关键字比对:" + item.get("text") + " -- 错误 -- 得分0");
@@ -150,7 +152,7 @@ public class JudgementServiceImpl implements JudgementService
Map<String, Object> item = new HashMap<>();
// 使用C99 运行并得出结果
String code_return = JudgementCUtils.run_code(pathC,code, examQuestionAnswer.getContentIn(),"-std=c99",null);
String code_return = JudgementCUtils.run_codes(pathC,code, examQuestionAnswer.getContentIn(),"-std=c99",null);
if (!isPassIndex && !code_return.contains("error")) {
// 编译没有报错,加上编译分数
totalScore += pass_score;
@@ -226,7 +228,7 @@ public class JudgementServiceImpl implements JudgementService
boolean isPassIndex = false;
for (ExamQuestionAnswer examQuestionAnswer : examQuestion.getAnswerList()) {
// 使用C99 运行并得出结果
String code_return = JudgementCUtils.run_code(pathC,code, null,"-std=c99",null);
String code_return = JudgementCUtils.run_codes(pathC,code, null,"-std=c99",null);
if (!isPassIndex && !code_return.contains("error")) {
// 编译没有报错,加上编译分数
totalScore += pass_score;
@@ -277,7 +279,7 @@ public class JudgementServiceImpl implements JudgementService
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 使用c99标准进行编译...");
// 使用C99 运行并得出结果
String code_return = JudgementCUtils.run_code(pathC,code,null,"-std=c99", "编译通过运行");
String code_return = JudgementCUtils.run_codes(pathC,code,null,"-std=c99", "编译通过运行");
if (!code_return.contains("error")) {
// 编译没有报错,加上编译分数
totalScore += pass_score;
@@ -331,4 +333,6 @@ public class JudgementServiceImpl implements JudgementService
private static int countOccurrences(String str, String subStr) {
return str.split(subStr, -1).length - 1;
}
}

View File

@@ -9,6 +9,7 @@ import java.nio.charset.UnmappableCharacterException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Stream;
@@ -40,6 +41,8 @@ public class JudgementCUtils
try (FileWriter writer = new FileWriter(file)) {
writer.write(code);
}
// 编译代码
ProcessBuilder compileBuilder = new ProcessBuilder("gcc", standard, programC, "-o", programOut);
@@ -115,79 +118,101 @@ public class JudgementCUtils
return "运行 C 代码时出错:" + ex.getMessage();
}
}
/**
* 运行C语言代码,出题测试运行
* @param code C语言代码
* @param standard 编译版本 C99
* @return 运行结果
*/
public static String run_test_code(String code, String standard) {
public static String run_codes(String pathC, String code, String input, String standard, String text) {
try {
// 写入 C 语言源码到文件
File file = new File("program.c");
try (FileWriter writer = new FileWriter(file)) {
boolean hasInput = code.contains("scanf") || code.contains("fgets") || code.contains("getchar");
File workDir = new File(pathC);
if (!workDir.exists()) workDir.mkdirs();
boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
String programC = new File(workDir, "program.c").getAbsolutePath();
String programOut = new File(workDir, isWindows ? "program.exe" : "program.out").getAbsolutePath();
// 写入 C 源码
try (FileWriter writer = new FileWriter(programC)) {
writer.write(code);
}
// 编译 C 代码
ProcessBuilder compileBuilder = new ProcessBuilder("gcc", standard, "program.c", "-o", "program.out");
// jar 所在目录
String homeDir = System.getProperty("user.dir");
File tccExe = new File(homeDir, isWindows ? "tcc/tcc.exe" : "tcc/tcc");
if (!tccExe.exists()) {
return "❌ 没有找到 tcc请确认在 jar 同级目录下有 tcc 文件夹";
}
// -std 参数
String stdFlag = null;
if (standard != null && !standard.trim().isEmpty()) {
String s = standard.trim();
stdFlag = s.startsWith("-std=") ? s : ("-std=" + s);
}
// 编译命令
ProcessBuilder compileBuilder;
if (stdFlag == null) {
compileBuilder = new ProcessBuilder(tccExe.getAbsolutePath(), programC, "-o", programOut);
} else {
compileBuilder = new ProcessBuilder(tccExe.getAbsolutePath(), stdFlag, programC, "-o", programOut);
}
compileBuilder.directory(workDir);
compileBuilder.redirectErrorStream(true);
Process compileProcess = compileBuilder.start();
StringBuilder compileOutput = new StringBuilder();
try (BufferedReader compileReader = new BufferedReader(new InputStreamReader(compileProcess.getInputStream()))) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(compileProcess.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = compileReader.readLine()) != null) {
compileOutput.append(line).append("\n");
}
while ((line = br.readLine()) != null) compileOutput.append(line).append("\n");
}
int compileResult = compileProcess.waitFor();
// 检查编译是否成功
String outputLower = compileOutput.toString().toLowerCase();
if (outputLower.contains("error:") || !Files.exists(Paths.get("program.out"))) {
return "编译失败,输出:\n" + compileOutput.toString();
if (compileResult != 0 || !new File(programOut).exists()) {
return "❌ 编译失败,输出:\n" + compileOutput;
}
// 运行程序并添加超时机制
ProcessBuilder runBuilder = new ProcessBuilder("./program.out");
// 运行
ProcessBuilder runBuilder = new ProcessBuilder(programOut);
runBuilder.directory(workDir);
runBuilder.redirectErrorStream(true);
Process runProcess = runBuilder.start();
if (hasInput && input != null) {
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(runProcess.getOutputStream()))) {
bw.write(input);
bw.newLine();
bw.flush();
}
}
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(runProcess.getInputStream()))) {
// 运行过程读取输出
try (BufferedReader br = new BufferedReader(
new InputStreamReader(runProcess.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
while ((line = br.readLine()) != null) output.append(line).append("\n");
}
return output.toString();
});
String output;
try {
output = future.get(5, TimeUnit.SECONDS); // 设置 5 秒超时
output = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
runProcess.destroy();
runProcess.destroyForcibly();
future.cancel(true);
output = "程序运行超时(超过 5 秒)";
output = "程序运行超时(超过 5 秒)";
} finally {
executor.shutdownNow();
}
return output;
} catch (Exception ex) {
ex.printStackTrace();
return "运行 C 代码时出错:" + ex.getMessage();
}
}
/**
* 读取文件代码
* @param filePath 文件路径