|
|
@ -26,7 +26,13 @@ public class WordQuestionParser { |
|
|
private static final Pattern OPTION_PATTERN = Pattern.compile("^[A-Z][..\\u3001::、]\\s*"); |
|
|
private static final Pattern OPTION_PATTERN = Pattern.compile("^[A-Z][..\\u3001::、]\\s*"); |
|
|
private static final Pattern ANSWER_PATTERN = Pattern.compile("^答案[::\\u3001]\\s*(.*)"); |
|
|
private static final Pattern ANSWER_PATTERN = Pattern.compile("^答案[::\\u3001]\\s*(.*)"); |
|
|
private static final Pattern ANALYSIS_PATTERN = Pattern.compile("^解析[::\\u3001]\\s*(.*)"); |
|
|
private static final Pattern ANALYSIS_PATTERN = Pattern.compile("^解析[::\\u3001]\\s*(.*)"); |
|
|
|
|
|
// 难度行:如"难度:中",不属于题干内容,忽略
|
|
|
|
|
|
private static final Pattern DIFFICULTY_PATTERN = Pattern.compile("^难度[::\\u3001]"); |
|
|
private static final Pattern SCORING_POINT_PATTERN = Pattern.compile("^得分点[::\\u3001]\\s*(.*)"); |
|
|
private static final Pattern SCORING_POINT_PATTERN = Pattern.compile("^得分点[::\\u3001]\\s*(.*)"); |
|
|
|
|
|
// 单条得分点行:得分点N. 内容(N 后可为英文点/中文点/顿号)
|
|
|
|
|
|
private static final Pattern SCORING_POINT_ITEM_PATTERN = Pattern.compile("^得分点[0-9]+[..、]"); |
|
|
|
|
|
// 题型标签:[单选题] [多选题] [判断题] [简答题]
|
|
|
|
|
|
private static final Pattern TYPE_TAG_PATTERN = Pattern.compile("\\[[^\\]]*题\\]"); |
|
|
|
|
|
|
|
|
private final MinIoUtils minIoUtils; |
|
|
private final MinIoUtils minIoUtils; |
|
|
|
|
|
|
|
|
@ -36,6 +42,8 @@ public class WordQuestionParser { |
|
|
private int imageCounter = 0; |
|
|
private int imageCounter = 0; |
|
|
/** 是否正在解析得分点段落(得分点触发后,后续段落归入得分点内容) */ |
|
|
/** 是否正在解析得分点段落(得分点触发后,后续段落归入得分点内容) */ |
|
|
private boolean parsingScoringPoints = false; |
|
|
private boolean parsingScoringPoints = false; |
|
|
|
|
|
/** 是否正在解析简答题参考答案正文(得分点结束后到下一道真题之间的内容) */ |
|
|
|
|
|
private boolean inReferenceAnswer = false; |
|
|
|
|
|
|
|
|
public WordQuestionParser(MinIoUtils minIoUtils) { |
|
|
public WordQuestionParser(MinIoUtils minIoUtils) { |
|
|
this.minIoUtils = minIoUtils; |
|
|
this.minIoUtils = minIoUtils; |
|
|
@ -81,9 +89,15 @@ public class WordQuestionParser { |
|
|
|
|
|
|
|
|
// 题号匹配
|
|
|
// 题号匹配
|
|
|
if (QUESTION_PATTERN.matcher(text).find()) { |
|
|
if (QUESTION_PATTERN.matcher(text).find()) { |
|
|
// 如果正在解析得分点,且题干极短,说明是得分点子项(如"1、两块纳米黑板")
|
|
|
// 带题型标签(如 40、[简答题])的一定是新题目,即使处于得分点/参考答案状态
|
|
|
// 归入最后一个得分点而非新题目
|
|
|
boolean isRealQuestion = TYPE_TAG_PATTERN.matcher(text).find(); |
|
|
if (parsingScoringPoints && currentQuestion != null |
|
|
// 参考答案正文中的编号行(如"1、LED高清显示系统")不是新题,归入当前简答题参考答案
|
|
|
|
|
|
if (!isRealQuestion && inReferenceAnswer && currentQuestion != null) { |
|
|
|
|
|
appendReferenceAnswer(currentQuestion, text); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
// 处于得分点解析状态,且不是真题目,归入得分点子项
|
|
|
|
|
|
if (!isRealQuestion && parsingScoringPoints && currentQuestion != null |
|
|
&& !currentQuestion.scoringPoints.isEmpty()) { |
|
|
&& !currentQuestion.scoringPoints.isEmpty()) { |
|
|
accumulateScoringPointContent(text, currentQuestion); |
|
|
accumulateScoringPointContent(text, currentQuestion); |
|
|
return; |
|
|
return; |
|
|
@ -93,6 +107,7 @@ public class WordQuestionParser { |
|
|
} |
|
|
} |
|
|
currentQuestion = new ParsedQuestion(); |
|
|
currentQuestion = new ParsedQuestion(); |
|
|
parsingScoringPoints = false; // 新题目,重置得分点状态
|
|
|
parsingScoringPoints = false; // 新题目,重置得分点状态
|
|
|
|
|
|
inReferenceAnswer = false; // 新题目,重置参考答案状态
|
|
|
// 去掉题号
|
|
|
// 去掉题号
|
|
|
currentQuestion.stem = QUESTION_PATTERN.matcher(text).replaceFirst("").trim(); |
|
|
currentQuestion.stem = QUESTION_PATTERN.matcher(text).replaceFirst("").trim(); |
|
|
currentQuestion.questionType = detectQuestionType(currentQuestion.stem); |
|
|
currentQuestion.questionType = detectQuestionType(currentQuestion.stem); |
|
|
@ -109,6 +124,12 @@ public class WordQuestionParser { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 正在解析参考答案正文:全部归入 referenceAnswer,不污染题干/选项
|
|
|
|
|
|
if (inReferenceAnswer) { |
|
|
|
|
|
appendReferenceAnswer(currentQuestion, text); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 答案行
|
|
|
// 答案行
|
|
|
Matcher answerMatcher = ANSWER_PATTERN.matcher(text); |
|
|
Matcher answerMatcher = ANSWER_PATTERN.matcher(text); |
|
|
if (answerMatcher.matches()) { |
|
|
if (answerMatcher.matches()) { |
|
|
@ -126,6 +147,11 @@ public class WordQuestionParser { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 难度行(如"难度:中"):元数据,不属于题干,直接忽略
|
|
|
|
|
|
if (DIFFICULTY_PATTERN.matcher(text).find()) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 得分点行(格式:得分点:得分点1. xxx 得分点2. xxx)
|
|
|
// 得分点行(格式:得分点:得分点1. xxx 得分点2. xxx)
|
|
|
Matcher scoringPointMatcher = SCORING_POINT_PATTERN.matcher(text); |
|
|
Matcher scoringPointMatcher = SCORING_POINT_PATTERN.matcher(text); |
|
|
if (scoringPointMatcher.matches()) { |
|
|
if (scoringPointMatcher.matches()) { |
|
|
@ -250,27 +276,32 @@ public class WordQuestionParser { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 将后续段落内容归入得分点(得分点跨段落时使用) |
|
|
* 将后续段落内容归入得分点(得分点跨段落时使用) |
|
|
|
|
|
* 只接受以"得分点N."开头的行;遇到不以"得分点"开头的普通正文 |
|
|
|
|
|
* (参考答案)则结束得分点收集,避免参考答案污染得分点 |
|
|
*/ |
|
|
*/ |
|
|
private void accumulateScoringPointContent(String text, ParsedQuestion question) { |
|
|
private void accumulateScoringPointContent(String text, ParsedQuestion question) { |
|
|
// 检查是否是新得分点标题(如:得分点3. xxx)
|
|
|
// 新得分点行(如:得分点3. xxx):交由 parseScoringPoints 处理(它会追加到已有列表)
|
|
|
Matcher m = Pattern.compile("得分点([0-9]+)[..、]\\s*(.*)").matcher(text); |
|
|
if (SCORING_POINT_ITEM_PATTERN.matcher(text).find()) { |
|
|
if (m.matches()) { |
|
|
|
|
|
// 新得分点:通过 parseScoringPoints 处理(它会处理保存逻辑)
|
|
|
|
|
|
// 这里把单条"得分点N. 内容"包装后调用
|
|
|
|
|
|
parseScoringPoints(text, question); |
|
|
parseScoringPoints(text, question); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 普通内容:追加到最后一个得分点
|
|
|
// 不是得分点行:视为参考答案正文开始,结束得分点收集,不再污染得分点
|
|
|
if (!question.scoringPoints.isEmpty()) { |
|
|
parsingScoringPoints = false; |
|
|
ParsedScoringPoint last = question.scoringPoints.get(question.scoringPoints.size() - 1); |
|
|
inReferenceAnswer = true; // 进入参考答案正文,后续编号行不再误判为新题
|
|
|
if (last.getContent() != null && !last.getContent().isEmpty()) { |
|
|
appendReferenceAnswer(question, text); |
|
|
last.setContent(last.getContent() + " " + text); |
|
|
|
|
|
} else { |
|
|
|
|
|
last.setContent(text); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** 追加参考答案正文(单独存放,不污染题干) */ |
|
|
|
|
|
private void appendReferenceAnswer(ParsedQuestion question, String text) { |
|
|
|
|
|
if (question == null) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (question.referenceAnswer == null || question.referenceAnswer.isEmpty()) { |
|
|
|
|
|
question.referenceAnswer = text; |
|
|
|
|
|
} else { |
|
|
|
|
|
question.referenceAnswer += "\n" + text; |
|
|
} |
|
|
} |
|
|
// 如果还没有得分点(异常情况),忽略
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -339,6 +370,8 @@ public class WordQuestionParser { |
|
|
List<ParsedOption> options = new ArrayList<>(); |
|
|
List<ParsedOption> options = new ArrayList<>(); |
|
|
String answer; |
|
|
String answer; |
|
|
String analysis; |
|
|
String analysis; |
|
|
|
|
|
/** 简答题参考答案正文(得分点之后、下一道题之前的内容) */ |
|
|
|
|
|
String referenceAnswer; |
|
|
/** 简答题得分点列表 */ |
|
|
/** 简答题得分点列表 */ |
|
|
List<ParsedScoringPoint> scoringPoints = new ArrayList<>(); |
|
|
List<ParsedScoringPoint> scoringPoints = new ArrayList<>(); |
|
|
} |
|
|
} |
|
|
|