diff --git a/src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java b/src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java index d7db601..ca1b862 100644 --- a/src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java +++ b/src/main/java/com/example/exam/exam/service/c/JudgementServiceImpl.java @@ -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 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; } + + } diff --git a/src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java b/src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java index 638e8ba..e7a8562 100644 --- a/src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java +++ b/src/main/java/com/example/exam/exam/utils/c/JudgementCUtils.java @@ -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 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 文件路径