【修改】 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.dal.ExamQuestionKeyword;
import com.example.exam.exam.utils.HtmlAppender; import com.example.exam.exam.utils.HtmlAppender;
import com.example.exam.exam.utils.c.JudgementCUtils; import com.example.exam.exam.utils.c.JudgementCUtils;
import com.example.exam.exam.utils.c.LogFileUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.example.exam.exam.dal.SourceAndText; import com.example.exam.exam.dal.SourceAndText;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -123,7 +123,9 @@ public class JudgementServiceImpl implements JudgementService
// 每个选项分值 = 总分 / 总权重 // 每个选项分值 = 总分 / 总权重
true_number += 1; true_number += 1;
key_score += one_keyword_score * Integer.parseInt((String) item.get("score_rate")); 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 { } else {
// 关键字不正确 // 关键字不正确
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ 关键字比对:" + item.get("text") + " -- 错误 -- 得分0"); judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "❌ 关键字比对:" + item.get("text") + " -- 错误 -- 得分0");
@@ -150,7 +152,7 @@ public class JudgementServiceImpl implements JudgementService
Map<String, Object> item = new HashMap<>(); Map<String, Object> item = new HashMap<>();
// 使用C99 运行并得出结果 // 使用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")) { if (!isPassIndex && !code_return.contains("error")) {
// 编译没有报错,加上编译分数 // 编译没有报错,加上编译分数
totalScore += pass_score; totalScore += pass_score;
@@ -226,7 +228,7 @@ public class JudgementServiceImpl implements JudgementService
boolean isPassIndex = false; boolean isPassIndex = false;
for (ExamQuestionAnswer examQuestionAnswer : examQuestion.getAnswerList()) { for (ExamQuestionAnswer examQuestionAnswer : examQuestion.getAnswerList()) {
// 使用C99 运行并得出结果 // 使用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")) { if (!isPassIndex && !code_return.contains("error")) {
// 编译没有报错,加上编译分数 // 编译没有报错,加上编译分数
totalScore += pass_score; totalScore += pass_score;
@@ -277,7 +279,7 @@ public class JudgementServiceImpl implements JudgementService
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 使用c99标准进行编译..."); judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 使用c99标准进行编译...");
// 使用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")) { if (!code_return.contains("error")) {
// 编译没有报错,加上编译分数 // 编译没有报错,加上编译分数
totalScore += pass_score; totalScore += pass_score;
@@ -331,4 +333,6 @@ public class JudgementServiceImpl implements JudgementService
private static int countOccurrences(String str, String subStr) { private static int countOccurrences(String str, String subStr) {
return str.split(subStr, -1).length - 1; 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.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -40,6 +41,8 @@ public class JudgementCUtils
try (FileWriter writer = new FileWriter(file)) { try (FileWriter writer = new FileWriter(file)) {
writer.write(code); writer.write(code);
} }
// 编译代码 // 编译代码
ProcessBuilder compileBuilder = new ProcessBuilder("gcc", standard, programC, "-o", programOut); ProcessBuilder compileBuilder = new ProcessBuilder("gcc", standard, programC, "-o", programOut);
@@ -115,79 +118,101 @@ public class JudgementCUtils
return "运行 C 代码时出错:" + ex.getMessage(); return "运行 C 代码时出错:" + ex.getMessage();
} }
} }
public static String run_codes(String pathC, String code, String input, String standard, String text) {
/**
* 运行C语言代码,出题测试运行
* @param code C语言代码
* @param standard 编译版本 C99
* @return 运行结果
*/
public static String run_test_code(String code, String standard) {
try { try {
// 写入 C 语言源码到文件 boolean hasInput = code.contains("scanf") || code.contains("fgets") || code.contains("getchar");
File file = new File("program.c");
try (FileWriter writer = new FileWriter(file)) { 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); writer.write(code);
} }
// 编译 C 代码 // jar 所在目录
ProcessBuilder compileBuilder = new ProcessBuilder("gcc", standard, "program.c", "-o", "program.out"); 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); compileBuilder.redirectErrorStream(true);
Process compileProcess = compileBuilder.start(); Process compileProcess = compileBuilder.start();
StringBuilder compileOutput = new StringBuilder(); 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; String line;
while ((line = compileReader.readLine()) != null) { while ((line = br.readLine()) != null) compileOutput.append(line).append("\n");
compileOutput.append(line).append("\n");
}
} }
int compileResult = compileProcess.waitFor(); int compileResult = compileProcess.waitFor();
if (compileResult != 0 || !new File(programOut).exists()) {
// 检查编译是否成功 return "❌ 编译失败,输出:\n" + compileOutput;
String outputLower = compileOutput.toString().toLowerCase();
if (outputLower.contains("error:") || !Files.exists(Paths.get("program.out"))) {
return "编译失败,输出:\n" + compileOutput.toString();
} }
// 运行程序并添加超时机制 // 运行
ProcessBuilder runBuilder = new ProcessBuilder("./program.out"); ProcessBuilder runBuilder = new ProcessBuilder(programOut);
runBuilder.directory(workDir);
runBuilder.redirectErrorStream(true); runBuilder.redirectErrorStream(true);
Process runProcess = runBuilder.start(); 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(); ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> { Future<String> future = executor.submit(() -> {
StringBuilder output = new StringBuilder(); 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; String line;
while ((line = reader.readLine()) != null) { while ((line = br.readLine()) != null) output.append(line).append("\n");
output.append(line).append("\n");
}
} }
return output.toString(); return output.toString();
}); });
String output; String output;
try { try {
output = future.get(5, TimeUnit.SECONDS); // 设置 5 秒超时 output = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) { } catch (TimeoutException e) {
runProcess.destroy(); runProcess.destroyForcibly();
future.cancel(true); future.cancel(true);
output = "程序运行超时(超过 5 秒)"; output = "程序运行超时(超过 5 秒)";
} finally { } finally {
executor.shutdownNow(); executor.shutdownNow();
} }
return output; return output;
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
return "运行 C 代码时出错:" + ex.getMessage(); return "运行 C 代码时出错:" + ex.getMessage();
} }
} }
/** /**
* 读取文件代码 * 读取文件代码
* @param filePath 文件路径 * @param filePath 文件路径