【修改】 C语言使用TCC进行编译,无需配置环境变量
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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 文件路径
|
||||||
|
Reference in New Issue
Block a user