PTA作业 答题程序1-3 阶段性总结Blog
前言
1、知识点:
题目集1:
7-5 答题判题程序1:涉及到正则表达式的应用,要对输入的字符串进行正确的拆分和解析,然后进行判断是否答对或者格式有没有错误,最后输出结果或者错误提示信息。
题目集2:
7-4 答题判题程序2:在答题判题程序1的基础上加深正则表达式的应用,并且引入了试卷信息的输入,此外还增加了乱序输入的情况,这就要求必须要存储到合适的数据结构中如ArrayList或者HashMap来进行判断了。程序在逻辑判断,格式判断,错误处理上更加严格、复杂了,必须加以小心。
题目集3:
7-3 答题判题程序3:在答题判题程序2的基础上更进一步加深正则表达式的应用,增加了学生信息、删除题目信息的输入,需要用到更多、更加准确的正则表达式来解析字符串,需要更加细心。还要判断题目是否被删除了,这就涉及到题目的有效性。
2、题量:
题目集1的题量还算比较多;题目集2的题量较题目集1少了一点;题目集3的题目是最少的,但是要写很久。
3、难度
题目集1总体难度不高;题目集2题目变少了但是难度变大了一点;题目集3的第二小题和第三小题都有一定难度,尤其是第三小题答题判题程序3,我在写的时候很头疼,类和类之间的关系非常的混乱。
4、代码量
答题判题程序1总的难度不高,代码量比较正常;答题判题程序2代码量增加了一些;答题判题程序3的代码量增加的挺多。
设计与分析
题目集1:7-5
题目要求输入题目信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。
程序中我共设计了三个主要类:分别是试卷类、题目类、答卷类。由于仅有两种格式的输入(题目,答案),因此可认为试卷只有一张。
题目类:表示题目,包括题目的编号、内容和标准答案。提供了检查学生答案是否正确的方法。
试卷类:管理一份试卷,其中包含多个题目。提供了添加题目的功能以及获取题目信息的方法。
答卷类:记录学生的作答情况,并评估答案的正确性。提供结果输出功能。
程序之间的依赖关系:
TestPaper类内部含有多个Question对象,使用Map来关联题目编号与题目对象之间的关系。
AnswerSheet类需要一个TestPaper对象进行评估操作,这样才能确定学生的答案是否正确。
核心功能分析:
1、题目处理:
利用HashMap存储题目,便于根据题号快速查找。
题目的标准答案和内容的处理用trim()去除多余的空格。这是我在经历多次数字格式异常如NumberFormatException后才上网查到要使用的。
用正则表达式分析输入格式。
以下是保存题目数量、题目编号、题目内容、标准答案的正则表达式分析:
点击查看代码
TestPaper testPaper = new TestPaper();
int questionCount = Integer.parseInt(input.nextLine().trim());//读取题目数量
for (int i = 0; i < questionCount; i++) { //循环读取题目
String line = input.nextLine();
String[] parts = line.split("#");
int number = Integer.parseInt(parts[1].split(":")[1].trim());//解析题目编号、内容和标准答案
String content = parts[2].split(":")[1].trim();
String answer = parts[3].split(":")[1].trim();
Question question = new Question(number, content, answer);
testPaper.addQuestion(question);
点击查看代码
String answerLine = input.nextLine().trim();
while (!"end".equals(answerLine)) {
String[] answerParts = answerLine.split("#");
for (int j = 1; j < answerParts.length; j++) {
String answer = answerParts[j].split(":")[1].trim(); //提取答案并添加到AnswerSheet中
answerSheet.addAnswer(answer);
}
answerLine = input.nextLine().trim();
}
2、答题与判题:
答题信息存储在List中,便于按顺序访问。
使用evaluateAnswers()方法逐一比较用户答案和标准答案。
3、输出处理:
利用String.join()拼接布尔值,简化输出。
sourcemonitor的生成报表:
相关类图:
题目集2:7-4
答题程序2的难度相较答题程序1有了较大的提升,因为第一次的设计比较不合理,导致难以在第一次的基础上进行代码的修改。答题程序2加入了试卷信息,并且还可能出现三种信息的乱序输入,导致不能实时对接收的信息进行判断,所以应该要接收输入后并存储,最后再来进行判断。答题程序2在输入处理上更为灵活,支持题目、试卷和答卷的乱序输入,因此更加难。
程序中共设计了三个主要类:分别是试卷类、题目类、答卷类。
题目类:表示一个问题,包括问题的编号、问题内容和正确答案。
试卷类:表示一份试卷,包含与试题相关的分数信息。
答题类:表示答题纸,记录学生的回答情况。
程序之间的依赖关系:
TestPaper 类通过 scores 属性存储与题目 ID 相关的分数。因此,TestPaper 直接使用 Question 的 ID 来标识题目并与其分数绑定。
AnswerSheet 中使用 testPaperId 属性来标识关联的试卷。每份答题纸的答案是针对特定试卷的。
总体上来说就是Question 处理问题信息,TestPaper 管理试卷及其分数,AnswerSheet 记录学生的答案,而 Main 控制整个流程。
核心功能分析:
- 输入读取与解析
程序通过loadInput方法将输入读取并解析为题目、试卷和答案,对于题目、试卷和答案的读取,我并没有使用复杂的正则表达式,而是利用字符串的简单条件判断和字符串操作(如startsWith和split)来处理。此方法能够高效且直观地解析输入。关键代码如下:
点击查看代码
if (line.startsWith("#N:")) {
addQuestion(line, questionList);
} else if (line.startsWith("#T:")) {
addTestPaper(line, testPapersMap);
} else if (line.startsWith("#S:")) {
addAnswerSheet(line, answerSheetList);
}
点击查看代码
String[] parts = line.split(" ");
int id = Integer.parseInt(parts[0].substring(3));
String questionText = Arrays.stream(parts, 1, parts.length - 1)
.map(part -> part.substring(3))
.collect(Collectors.joining(" "));
String answer = parts[parts.length - 1].substring(3);
questionList.add(new Question(id, questionText, answer));
对字符串的处理:
通过split(" ")分割字符串,解析题号、题目内容和标准答案。
使用substring来构建题目内容,简化了拼接过程。
- 答卷处理与得分评估
点击查看代码
for (AnswerSheet answerSheet : answerSheets) {
TestPaper testPaper = testPapersMap.get(answerSheet.getTestPaperId());
if (testPaper == null) {
System.out.println("The test paper number does not exist");
continue;
}
List<String> results = new ArrayList<>();
List<Integer> scoreList = new ArrayList<>();
List<Integer> questionIds = testPaper.listQuestionIds();
for (int questionId : questionIds) {
Optional<Question> questionOpt = questions.stream()
.filter(q -> q.getId() == questionId)
.findFirst();
if (questionOpt.isPresent()) {
Question question = questionOpt.get();
processAnswer(question, answerSheet, questionId, results, scoreList, testPaper);
} else {
results.add("answer is null");//如果输入的答案信息少于试卷的题目数量,没有答案信息的题目输"answer is null"
scoreList.add(0);
}
}
相关类图:
sourcemonitor生成报表:
题目集3 7-3
答题程序3再难度上更上一层楼,让我感觉非常的吃力,因为又加上了学生信息、删除题目信息,并且也有乱序输入的问题,因此也是要存储再判断的。题目增加了许多非法输入的判断,需根据不同的情况输出不同的信息,导致在写代码时思路很乱,在最后也没能通过全部测试点,但是让我对正则表达式有了更加深入的理解。在我的设计中,我在答题程序2的基础上增加了试卷管理类和问题管理类还有学生类和学生管理类,以实现对试卷和问题的集中管理,并能有效记录和处理学生的答题信息,各个类之间的责任就可以比较明确地划分,便于写代码。
正确的思路应该是:
1、在处理输入时,首先针对每种输入格式使用正则表达式进行匹配。如果输入格式不符合预期,输出错误提示并跳过该行的处理逻辑。
2、在添加试卷信息后,计算试卷的总分,并检查是否为100分。若不为100分,则输出警示信息。
3、从答卷管理类中获取所有答卷信息,对于每一份答卷,首先获取试卷编号。查询试卷管理类,检查试卷编号是否在有效试卷列表中,如果试卷不存在,输出提示信息,并跳过该答卷的后续处理。
4、若试卷存在,从试卷管理类获取试卷的题目信息,检查题目编号是否在题库中存在。查询题目管理类,检查题目编号是否在有效题目列表中。如果题目不存在,输出相关信息。
5、判断答卷中是否有答案,如果为空或者缺失,输出提示信息。如果题目被删除了,输出信息。
6、将学生的答案和标准答案对比,判断是否答对,记录得分的情况。
7、完成所有题目的批改后,获取该学生的学号,查询学生管理类,确保该学生在记录中存在。如果该学生不存在,输出相关信息。
8、在确认学生存在后,按格式输出总答卷成绩,包括每道题的得分、总分以及正确和错误的题目数量等信息。
程序中我设计了7个类,分别是题目类、题目管理类、试卷类、试卷管理类、学生类、学生管理类、答卷类。
题目类:表示一个题目,包括题目编号、题目内容、正确答案和有效性(是否被删除了)。
题目管理类:管理所有的题目,提供添加、查找、状态更新等功能。
试卷类:表示一份试卷,包含试卷的编号、对应题目 ID 和分值。
试卷管理类:管理所有的试卷,提供添加、查找等功能。
学生类:表示一个学生,包括学生的 ID 和名称。
学生管理类:管理所有学生的信息,提供查找学生姓名的功能。
答卷类:表示学生的答卷,记录学生的答案和相关试卷信息。
程序之间的依赖关系:
QuestionMgr 类用于管理 Question 实例的集合,包括添加问题、检查问题有效性和获取问题信息等。
TestPaperMgr 管理 TestPaper 类的集合,包括添加、查找和更新试卷。
AnswerSheet 关联特定的 TestPaper 和 QuestionMgr,以便在输出结果时进行答案验证和评分。
StudentMgr 维护 Student 的列表,用于添加学生和通过学号查找姓名。
利用的正则表达式:
点击查看代码
matcher = Pattern.compile("#N:(\\d+) #Q:(.*?) #A:(.*)").matcher(line)).matches();////题目正则表达式
(matcher = Pattern.compile("#T:(\\d+) (.+)").matcher(line)).matches();////试卷正则表达式
(matcher = Pattern.compile("#X:(\\d+) (.+)").matcher(line)).matches();////学生正则表达式
(matcher = Pattern.compile("#S:(\\d+) (\\d+) (.+)").matcher(line)).matches();//答卷正则表达式
line.startsWith("#D:N-");//删除题目正则表达式
其他的代码和答题2的差的不多,也是输出错误提示信息或者输出格式化的信息。
核心功能解析:
- 输入读取与解析:
对输入的字符串进行正则表达式解析,存储到数据结构中,等之后取出判断。
2、题目管理:
QuestionMgr 类负责管理所有题目。它提供了添加题目、检查题目有效性和获取正确答案等方法。每个题目被封装在 Question 类中,包含题目编号、内容、答案和有效性信息。
3、 试卷管理
TestPaper 类用于表示一份试卷,管理试卷中的题目及其对应的分值。TestPaperMgr 则负责管理所有试卷的增删改查。试卷检查其总分是否为 100 分,以确保完整性。
4、学生管理:
StudentMgr 类负责管理学生信息,通过提供学生 ID 查找姓名等功能。学生信息存储在 Student 类中,包括学生的 ID 和姓名。
5、答卷处理与评分:
AnswerSheet 类实现学生答卷的管理,存储每道题的回答并计算最终得分。在方法 outputResults 中,程序遍历答题情况,验证每道题的有效性和正确性,并输出评估结果。若答题不完整或有无效题目,系统会输出相应提示。
相关类图:
sourcemonitor生产报表:
三、 踩坑心得
在这三次作业中,大量使用到了正则表达式,而输入的数据中,会存在空格字符,需要识别并去除空白字符,我第一次作业没注意空格符号的存在导致出现数字格式异常NumberFormatException的问题。并且在第三题的正则表达式解析当中我没有注意题目的格式和之前的答题程序2不一样,还是沿用答题程序2的正则表达式解析,导致解析有问题,比如#S:1 20201103 #A:1-5 #A:2-4和#S:1 #A:5 #A:4。
第二次代码中,应该是存储的数据结构有问题,越界了可能是,导致一直非零返回。

后面一直调试也没调试出来。
第三次作业中增加了删除题目的输入,开始的时候我用了一个删除类来进行题目删除的管理,但是后面发现没有必要,这会使程序更加复杂,实际上只需要在题目类中增添一个题目是否有效的属性即可解决。
点击查看代码
private int id;
private String content;
private String answer;
private boolean live; // 是否有效
在测试样例中,我通过了题目给出的10个样例,但是还有其他的一直输出不出来。

在单信息输入的情况下,应该直接输出格式错误的提示信息,但是我一直输出的是试卷不存在的信息,我认为应该是我的输出逻辑有问题。但是我修改完后能直接输出格式错误的提示信息了,就不能正确输出输出的是试卷不存在的信息了,导致两边都不行。其他的测试点没有通过大部分是我的异常处理的逻辑问题,不能输出正确的提示信息。在我设计代码的时候没有考虑周全,使用了不合适的数据结构来存储数据。还有类之间的关系以及引用太混乱,导致我的思路非常乱,造成了这个后果。在我的答题程序3的sourcemonitor生产报表中,可以看到我的Main类中复杂度非常的高,这很容易导致维护的困难和逻辑的错误。我在答题程序2中的Main类中也写了很多代码。这是我以后应该要多加注意的地方,不要在Main类里面写太多代码,造成读起来不畅并且写起来思路也不清晰的后果。
四、改进建议
1、在Main里面不应该有那么多的代码,将 main 方法中的逻辑分拆成若干个小而独立的方法,以减少复杂度,提高代码的可读性和易维护性,使结构更加清晰,便于理解,也方便我逻辑的顺畅,写代码更有逻辑。
2、应当增强输入验证,对输入格式进行更严格的检查,确保用户输入的格式符合预期。在出现错误时,能更好的打印错误信息。
3、记得写清楚注释,在关键逻辑和方法上添加注释,解释其功能和逻辑,方便后续维护与代码理解。
4、类的方法设计复杂,多种功能混合在一起,第一次和第二次的类比较少,导致方法的职责并不是非常单一,应该适当多增加一些类来存储信息,使得方法的职责更加单一,同时也可以增加可读性。
5、避免不必要的计算,在处理评分时,对未答题的情况可以进行一次性处理,减少循环中的额外判断。
6、可以使用Switch语句代替if-else语句,这样可读性更高,更高效。
五、总结
在三次作业中,我学到了正则表达式的使用、类的设计、把代码分成小块来进行、面象对象编程、封装、优化代码等知识,还提高了实际问题的分析、设计、实现与测试的能力。在作业中,还加深了我对如ArrayList、HashMap等数据结构的理解,以及它们的应用场景。对于正则表达式我还要再加强一下,了解的比较浅。对于HashMap的使用我也要深度学习一下,写到一半老是忘记它的方法怎么用。同时我也应该更加细心,才能更加快地学习。其他的没有什么建议。我期待在今后的学习中,能够应用这些知识,进一步探索更复杂的项目与技术。
浙公网安备 33010602011771号