一.前言
1.知识点:
(1)面向对象编程(OOP):这三道题目要求设计并实现题目类、试卷类和答卷类等,这涉及到类的定义、属性的封装以及方法的实现,是OOP的核心概念。
(2)方式设计:设计并实现用于判题、保存题目、保存答案等功能的方法,这涉及到方法的命名、参数设计以及逻辑实现。
(3)数据结构与集合:题目中涉及到对题目,试卷,学生信息和答卷的存储和管理,这可能需要使用到如ArrayList,HashMap等集合类来存储这些对象。
(4)字符串处理:题目中的输入格式是字符串形式,因此需要进行字符串的解析和处理,可以使用正则表达式对字符串进行处理简化代码,如分割字符串、提取子串等。
2.题量:
(1)答题判题程序1题量相对较小,是程序2和程序3的基础,题目本身只包含了一个主要的编程任务,即设计并实现答题判题程序。但从实现细节上看,需要完成题目类、试卷类和答卷类的定义和实现,以及相关的输入输出处理,所以实际还是具有一定的工作量。
(2)答题判题程序2在1的基础上又所增加了对试卷信息的处理,需要将试卷和一组题目关联起来,逻辑上更加复杂,题量适中,涵盖了基本的输入输出处理和逻辑判断。
(3)答题判题程序3这道题目包含了五种不同的输入信息,每种输入信息都需要单独处理,因此题量相对较大。同时,每种输入信息都有特定的格式约束和处理逻辑,这也增加了题目的复杂度。
3.难度:
(1)答题判题程序1难度适中。题目要求明确,且提供了详细的设计建议,这对于理解题目要求和进行程序设计很有帮助。
(2)答题判题程序2属于中等难度。需要具备基本的Java编程能力,熟悉字符串处理、数据结构和逻辑判断等方面的知识。同时,题目中的输入格式和输出格式都有一定的约束。此外,处理试卷总分警示和判题结果的部分也需要一定的逻辑思考和算法设计能力。
(3)答案判题程序3属于中等偏难级别。它在程序2的基础上,还需要我们具备较强的逻辑思维能力和问题解决能力。此外,题目中的删除题目信息部分也增加了处理的复杂性,需要在设计时考虑到这种情况,并正确处理相关的逻辑。
二.设计与分析
1.答题判题程序-1
7-1 答题判题程序-1 作者 蔡轲 单位 南昌航空大学 设计实现答题程序,模拟一个小型的测试,要求输入题目信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。 输入格式: 程序输入信息分三部分: 1、题目数量 格式:整数数值,若超过1位最高位不能为0, 样例:34 2、题目内容 一行为一道题,可以输入多行数据。 格式:"#N:"+题号+" "+"#Q:"+题目内容+" "#A:"+标准答案 格式约束:题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。 样例:#N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 3、答题信息 答题信息按行输入,每一行为一组答案,每组答案包含第2部分所有题目的解题答案,答案的顺序号与题目题号相对应。 格式:"#A:"+答案内容 格式约束:答案数量与第2部分题目的数量相同,答案之间以英文空格分隔。 样例:#A:2 #A:78 2是题号为1的题目的答案 78是题号为2的题目的答案 答题信息以一行"end"标记结束,"end"之后的信息忽略。 输出格式: 1、题目数量 格式:整数数值,若超过1位最高位不能为0, 样例:34 2、答题信息 一行为一道题的答题信息,根据题目的数量输出多行数据。 格式:题目内容+" ~"+答案 样例:1+1=~2 2+2= ~4 3、判题信息 判题信息为一行数据,一条答题记录每个答案的判断结果,答案的先后顺序与题目题号相对应。 格式:判题结果+" "+判题结果 格式约束: 1、判题结果输出只能是true或者false, 2、判题信息的顺序与输入答题信息中的顺序相同 样例:true false true 输入样例1: 单个题目。例如: 1 #N:1 #Q:1+1= #A:2 #A:2 end 输出样例1: 在这里给出相应的输出。例如: 1+1=~2 true 输入样例2: 单个题目。例如: 1 #N:1 #Q:1+1= #A:2 #A:4 end 输出样例2: 在这里给出相应的输出。例如: 1+1=~4 false 输入样例3: 多个题目。例如: 2 #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #A:2 #A:4 end 输出样例3: 在这里给出相应的输出。例如: 1+1=~2 2+2=~4 true true 输入样例4: 多个题目。例如: 2 #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #A:2 #A:2 end 输出样例4: 在这里给出相应的输出。例如: 1+1=~2 2+2=~2 true false 输入样例5: 多个题目,题号顺序与输入顺序不同。例如: 2 #N:2 #Q:1+1= #A:2 #N:1 #Q:5+5= #A:10 #A:10 #A:2 end 输出样例5: 在这里给出相应的输出。例如: 5+5=~10 1+1=~2 true true 输入样例6: 含多余的空格符。例如: 1 #N:1 #Q: The starting point of the Long March is #A:ruijin #A:ruijin end 输出样例6: 在这里给出相应的输出。例如: The starting point of the Long March is~ruijin true 输入样例7: 含多余的空格符。例如: 1 #N: 1 #Q: 5 +5= #A:10 #A:10 end 输出样例7: 在这里给出相应的输出。例如: 5 +5=~10 true 设计建议: 以下是针对以上题目要求的设计建议,其中的属性、方法为最小集,实现代码中可根据情况添加所需的内容: 题目类(用于封装单个题目的信息): 属性:题目编号、题目内容、标准答案-standardAnswer 方法:数据读写set\get方法、 判题方法(答案-answer):判断答案-answer是否符合标准答案-standardAnswer 试卷类(用于封装整套题目的信息) 属性:题目列表(题目类的对象集合)、题目数量 方法:判题方法(题号-num、答案-answer):判断答案-answer是否符合对应题号的题目标准答案-standardAnswer 保存题目(题号-num、题目-question):将题目保存到题目列表中,保存位置与num要能对应 答卷类(用于封装答题信息) 属性:试卷(试卷类的对象)、答案列表(保存每一题的答案)、判题列表(保存每一题的判题结果true/false) 方法:判题方法(题号-num):判断答案列表中第num题的结果是否符合试卷中对应题号的题目标准答案 输出方法(题号-num):按照题目的格式要求,输出题号为num的题目的内容和答题结果。 保存一个答案(题号-num,答案-answer):保存题号为num的题目的答题结果answer。
设计
我设计了题目类,试卷类,答卷类,处理字符串类这4个类来实现该程序的功能
(1)题目类,用于储存一道题目的题号,内容,答案
class Question { private int id; private String content; private String answer; //有参构造 public Question(int id, String content, String answer) { this.id = id; this.content = content; this.answer = answer; }
(2)试卷类:第一题中的由于是单张试卷,且不需要对试卷进行什么操作,故该类只需进行很简单的创建一张试卷的操作(我故将在程序2中展示代码)
(3)答卷类:储存单张试卷中回答的每题答案,该属性使用动态数组使得答案的个数可以根据输入的个数而灵活变动,主要实现判题功能
class AnswerSheet { private List<String> answers=new ArrayList<>(); //构造 public AnswerSheet() { } public AnswerSheet(List<String> answers) { this.answers = answers; } //get set public void setAnswers(List<String> answers) { this.answers = answers; } public void addAnswer(String answer) { answers.add(answer); } //得到一个包含所有答案的字符串数组。(将List<String>类型的转化为String[]) public String[] getAnswers() { return answers.toArray(new String[0]); } public boolean[] checkAnswers(Map<Integer, Question> questionsById) { boolean[] results = new boolean[answers.size()]; int index = 0; for (int questionId : questionsById.keySet()) { if (index < answers.size() && answers.get(index) != null) { Question question = questionsById.get(questionId); results[index] = question.getAnswer().equals(answers.get(index)); } else { results[index] = false; //没有答案或者超出范围 } index++; } return results; }
(4)处理字符串类:这个类是我在后续写题的过程中添加的,随着每次题目对字符串输入难度的加大,我认为创建这个类还是很有必要,它可以实现使用正则表达式匹配输入的字符串
//用于对输入字符串进行解析 class RegexParser { private Map<Integer, Question> questionsById = new LinkedHashMap<>(); private AnswerSheet answerSheet = new AnswerSheet(); //构造 public RegexParser() { } public RegexParser(Map<Integer, Question> questionsById, AnswerSheet answerSheet) { this.questionsById = questionsById; this.answerSheet = answerSheet; } //get set public Map<Integer, Question> getQuestionsById() { return questionsById; } public void setQuestionsById(Map<Integer, Question> questionsById) { this.questionsById = questionsById; } public AnswerSheet getAnswerSheet() { return answerSheet; } public void setAnswerSheet(AnswerSheet answerSheet) { this.answerSheet = answerSheet; } //判断是否合法 public boolean ifvaild(String line) { //匹配题目信息格式 if (Pattern.compile("#N:(\\d+) #Q:(.*?) #A:(\\d+)").matcher(line).matches()) { return true; } // 匹配试卷信息格式 else if (Pattern.compile("#T:(\\d+) (.+)").matcher(line).matches()) { return true; } // 匹配答卷信息格式 else if (Pattern.compile("#S:(\\d+) (\\d+) (.+)").matcher(line).matches()) { return true; } // 如果都不匹配,则返回false return false; } //匹配字符串(不合法时输出错误) public void matchString(String line) { if (ifvaild(line)) { //输入题目信息 if (line.startsWith("#N:")) { String[] parts = line.split(" "); int id = 0; String content = null; String answer = null; for (String part : parts) { if (part.startsWith("#N:")) { String[] idParts = part.split(":"); id = Integer.parseInt(idParts[1].trim()); } else if (part.startsWith("#Q:")) { content = part.substring("#Q:".length()).trim(); } else if (part.startsWith("#A:")) { answer = part.substring("#A:".length()).trim(); } } questionsById.put(id, new Question(id, content, answer)); } //输入答卷信息 else if (line.startsWith("#A:")) { String[] parts = line.substring("#A:".length()).trim().split(" #A:"); // 注意这里的修改 for (String part : parts) { if (!part.isEmpty()) { // 跳过空字符串(例如,如果行以 #A: 开头,但后面没有跟随任何内容) this.answerSheet.addAnswer(part.trim()); } } }else { System.out.println("wrong format:" + line); } } } }
(5)Main方法:在将多个题目与试卷进行联系的时候,我使用了HashMap的数据结构。
public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); String line; int num = input.nextInt(); RegexParser regexParser = new RegexParser(); while (!(line = input.nextLine()).equals("end")) { regexParser.matchString(line); } Map<Integer,Question>questionsById = regexParser.getQuestionsById(); AnswerSheet answerSheet = regexParser.getAnswerSheet(); TestPaper test = new TestPaper(); test.setQuestionNum(num); String[] answer =answerSheet.getAnswers(); boolean[] results = answerSheet.checkAnswers(questionsById); for (int i=0;i<num;i++) { Question question = questionsById.get(i); if (question != null) { String content = question.getContent(); if (content != null) { System.out.println(content + "~" + answer[i ]); } } } for(int i=0;i<num;i++){ System.out.println(results[i] ); } } }
我的类图
分析结果
2.答题判题程序-2
7-2 答题判题程序-2 分数 54 作者 蔡轲 单位 南昌航空大学 设计实现答题程序,模拟一个小型的测试,以下粗体字显示的是在答题判题程序-1基础上增补或者修改的内容。 要求输入题目信息、试卷信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。 输入格式: 程序输入信息分三种,三种信息可能会打乱顺序混合输入: 1、题目信息 一行为一道题,可输入多行数据(多道题)。 格式:"#N:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案 格式约束: 1、题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。 2、允许题目编号有缺失,例如:所有输入的题号为1、2、5,缺少其中的3号题。此种情况视为正常。 样例:#N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 2、试卷信息 一行为一张试卷,可输入多行数据(多张卷)。 格式:"#T:"+试卷号+" "+题目编号+"-"+题目分值 题目编号应与题目信息中的编号对应。 一行信息中可有多项题目编号与分值。 样例:#T:1 3-5 4-8 5-2 3、答卷信息 答卷信息按行输入,每一行为一张答卷的答案,每组答案包含某个试卷信息中的题目的解题答案,答案的顺序与试卷信息中的题目顺序相对应。 格式:"#S:"+试卷号+" "+"#A:"+答案内容 格式约束:答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。 样例:#S:1 #A:5 #A:22 1是试卷号 5是1号试卷的顺序第1题的题目答案 22是1号试卷的顺序第2题的题目答案 答题信息以一行"end"标记结束,"end"之后的信息忽略。 输出格式: 1、试卷总分警示 该部分仅当一张试卷的总分分值不等于100分时作提示之用,试卷依然属于正常试卷,可用于后面的答题。如果总分等于100分,该部分忽略,不输出。 格式:"alert: full score of test paper"+试卷号+" is not 100 points" 样例:alert: full score of test paper2 is not 100 points 2、答卷信息 一行为一道题的答题信息,根据试卷的题目的数量输出多行数据。 格式:题目内容+"~"+答案++"~"+判题结果(true/false) 约束:如果输入的答案信息少于试卷的题目数量,答案的题目要输"answer is null" 样例:3+2=~5~true 4+6=~22~false. answer is null 3、判分信息 判分信息为一行数据,是一条答题记录所对应试卷的每道小题的计分以及总分,计分输出的先后顺序与题目题号相对应。 格式:题目得分+" "+....+题目得分+"~"+总分 格式约束: 1、没有输入答案的题目计0分 2、判题信息的顺序与输入答题信息中的顺序相同 样例:5 8 0~13 根据输入的答卷的数量以上2、3项答卷信息与判分信息将重复输出。 4、提示错误的试卷号 如果答案信息中试卷的编号找不到,则输出”the test paper number does not exist”,参见样例9。 设计建议: 参考答题判题程序-1,建议增加答题类,类的内容以及类之间的关联自行设计。 输入样例1: 一张试卷一张答卷。试卷满分不等于100。例如: #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-5 2-8 #S:1 #A:5 #A:22 end 输出样例1: 在这里给出相应的输出。例如: alert: full score of test paper1 is not 100 points 1+1=~5~false 2+2=~22~false 0 0~0 输入样例2: 一张试卷一张答卷。试卷满分不等于100。例如: #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-70 2-30 #S:1 #A:5 #A:22 end 输出样例2: 在这里给出相应的输出。例如: 1+1=~5~false 2+2=~22~false 0 0~0 输入样例3: 一张试卷、一张答卷。各类信息混合输入。例如: #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-70 2-30 #N:3 #Q:3+2= #A:5 #S:1 #A:5 #A:4 end 输出样例: 在这里给出相应的输出。例如: 1+1=~5~false 2+2=~4~true 0 30~30 输入样例4: 试卷题目的顺序与题号不一致。例如: #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 2-70 1-30 #N:3 #Q:3+2= #A:5 #S:1 #A:5 #A:22 end 输出样例: 在这里给出相应的输出。例如: 2+2=~5~false 1+1=~22~false 0 0~0 输入样例5: 乱序输入。例如: #N:3 #Q:3+2= #A:5 #N:2 #Q:2+2= #A:4 #T:1 3-70 2-30 #S:1 #A:5 #A:22 #N:1 #Q:1+1= #A:2 end 输出样例: 在这里给出相应的输出。例如: 3+2=~5~true 2+2=~22~false 70 0~70 输入样例6: 乱序输入+两份答卷。例如: #N:3 #Q:3+2= #A:5 #N:2 #Q:2+2= #A:4 #T:1 3-70 2-30 #S:1 #A:5 #A:22 #N:1 #Q:1+1= #A:2 #S:1 #A:5 #A:4 end 输出样例: 在这里给出相应的输出。例如: 3+2=~5~true 2+2=~22~false 70 0~70 3+2=~5~true 2+2=~4~true 70 30~100 输入样例7: 乱序输入+分值不足100+两份答卷。例如: #N:3 #Q:3+2= #A:5 #N:2 #Q:2+2= #A:4 #T:1 3-7 2-6 #S:1 #A:5 #A:22 #N:1 #Q:1+1= #A:2 #S:1 #A:5 #A:4 end 输出样例: 在这里给出相应的输出。例如: alert: full score of test paper1 is not 100 points 3+2=~5~true 2+2=~22~false 7 0~7 3+2=~5~true 2+2=~4~true 7 6~13 输入样例8: 乱序输入+分值不足100+两份答卷+答卷缺失部分答案。例如: #N:3 #Q:3+2= #A:5 #N:2 #Q:2+2= #A:4 #T:1 3-7 2-6 #S:1 #A:5 #A:22 #N:1 #Q:1+1= #A:2 #T:2 2-5 1-3 3-2 #S:2 #A:5 #A:4 end 输出样例: 在这里给出相应的输出。例如: alert: full score of test paper1 is not 100 points alert: full score of test paper2 is not 100 points 3+2=~5~true 2+2=~22~false 7 0~7 2+2=~5~false 1+1=~4~false answer is null 0 0 0~0 输入样例9: 乱序输入+分值不足100+两份答卷+无效的试卷号。例如: #N:3 #Q:3+2= #A:5 #N:2 #Q:2+2= #A:4 #T:1 3-7 2-6 #S:3 #A:5 #A:4 end 输出样例: 在这里给出相应的输出。例如: alert: full score of test paper1 is not 100 points The test paper number does not exist
设计
在1的基础上,我对试卷类,解析字符串类的进行了更复杂的功能实现
(1)试卷类:在程序1的基础上,我对试卷类设计了实现求总分和使用Map将每一道题目都与相应的分数匹配在一起的操作
class TestPaper { private int id; //试卷序号 private LinkedHashMap<Integer, Integer> questionsAndScores; public TestPaper(int id) { this.id = id; this.questionsAndScores = new LinkedHashMap<>(); } public void addQuestion(int questionId, int score) { questionsAndScores.put(questionId, score); } public int getId() { return id; } public void setId(int id) { this.id = id; } public LinkedHashMap<Integer, Integer> getQuestionsAndScores() { return questionsAndScores; } public int oneScore(int i){ return questionsAndScores.get(i); } public void ifTotalScore() { int total = 0; for (int score : questionsAndScores.values()) { total += score; } if(total!=100){ System.out.printf("alert: full score of test paper%d is not 100 points\n",getId()); } } }
(2)解析字符串类:增加了一个对输入试卷信息的处理并且修改了对答卷信息的一些处理
import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; //用于对输入字符串进行解析 class RegexParser { Map<Integer, Question> questionsById = new LinkedHashMap<>(); Map<Integer, TestPaper> testPapersById = new HashMap<>(); Map<Integer,AnswerSheet> answerSheetsById = new HashMap<>(); //构造 public RegexParser() { } public RegexParser(Map<Integer, Question> questionsById, Map<Integer, TestPaper> testPapersById, Map<Integer, AnswerSheet> answerSheetsById) { this.questionsById = questionsById; this.testPapersById = testPapersById; this.answerSheetsById = answerSheetsById; } //get set public Map<Integer, Question> getQuestionsById() { return questionsById; } public void setQuestionsById(Map<Integer, Question> questionsById) { this.questionsById = questionsById; } public Map<Integer, TestPaper> getTestPapersById() { return testPapersById; } public void setTestPapersById(Map<Integer, TestPaper> testPapersById) { this.testPapersById = testPapersById; } public Map<Integer, AnswerSheet> getAnswerSheetsById() { return answerSheetsById; } public void setAnswerSheetsById(Map<Integer, AnswerSheet> answerSheetsById) { this.answerSheetsById = answerSheetsById; } //匹配字符串 public void matchString(String line) { if (line.startsWith("#N:")) { if(testPapersById.isEmpty()&&answerSheetsById.isEmpty()){ String[] parts = line.split(" "); int id=0; String content=null; String answer=null; for(String part : parts){ if(part.startsWith("#N:")){ String[] idParts = part.split(":"); id = Integer.parseInt(idParts[1].trim()); } else if(part.startsWith("#Q:")){ content = part.substring("#Q:".length()).trim(); } else if(part.startsWith("#A:")){ answer = part.substring("#A:".length()).trim(); } } questionsById.put(id, new Question(id, content, answer)); } } else if (line.startsWith("#T:")) { String[] parts = line.substring("#T:".length()).trim().split(" "); int id = Integer.parseInt(parts[0]); TestPaper testPaper = new TestPaper(id); for (int i = 1; i < parts.length; i++) { String[] qScoreParts = parts[i].split("-"); int questionId = Integer.parseInt(qScoreParts[0]); int score = Integer.parseInt(qScoreParts[1]); testPaper.addQuestion(questionId, score); testPapersById.put(id, testPaper); } } //输入答卷信息 else if (line.startsWith("#S:")) { String[] parts = line.substring("#S:".length()).trim().split(" "); int id = Integer.parseInt(parts[0]); AnswerSheet answersheet = new AnswerSheet(id); for (int i = 1; i < parts.length; i++) { if (parts[i].startsWith("#A:")) { String answer = parts[i].substring("#A:".length()).trim(); answersheet.addAnswer(answer); } } answerSheetsById.put(id,answersheet); } } }
(3)Main:在main方法中,利用Map将一套试卷以及一套试卷的答案整合好
Map<Integer,Question>questionsById = regexParser.getQuestionsById(); Map<Integer, TestPaper> testPapersById = regexParser.getTestPapersById(); Map<Integer,AnswerSheet> answerSheetsById =regexParser.getAnswerSheetsById();
再调用方法输出,其全部代码如下
public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); String line; RegexParser regexParser = new RegexParser(); while (!(line = input.nextLine()).equals("end")) { regexParser.matchString(line); } Map<Integer,Question>questionsById = regexParser.getQuestionsById(); Map<Integer, TestPaper> testPapersById = regexParser.getTestPapersById(); Map<Integer,AnswerSheet> answerSheetsById =regexParser.getAnswerSheetsById(); TestPaper test= testPapersById.get(1); test.ifTotalScore(); AnswerSheet answersheet=answerSheetsById.get(1); String[] answer =answersheet.getAnswers(); boolean[] results=answersheet.checkAnswers(questionsById); int total=0,i=0; LinkedHashMap<Integer, Integer> questionsAndScores = test.getQuestionsAndScores(); for(int questionId:questionsAndScores.keySet()) { String content=questionsById.get(questionId).getContent(); i++; System.out.println(content+"~"+answer[i-1]+"~"+results[i-1]); } int num = questionsById.size(); for(i=1;i<=num;i++){ if(i==1){ if(results[i-1]){ total =total+test.oneScore(i); System.out.printf("%d",test.oneScore(i)); } else{ System.out.printf("0"); } } else{ if(results[i-1]){ total =total+test.oneScore(i); System.out.printf(" %d",test.oneScore(i)); } else{ System.out.printf(" 0"); } } } System.out.printf("~%d",total); } }
我的类图
分析结果
3.答题判题程序-3
7-3 答题判题程序-3 分数 80 作者 蔡轲 单位 南昌航空大学 设计实现答题程序,模拟一个小型的测试,以下粗体字显示的是在答题判题程序-2基础上增补或者修改的内容,要求输入题目信息、试卷信息、答题信息、学生信息、删除题目信息,根据输入题目信息中的标准答案判断答题的结果。 输入格式: 程序输入信息分五种,信息可能会打乱顺序混合输入。 1、题目信息 题目信息为独行输入,一行为一道题,多道题可分多行输入。 格式:"#N:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案 格式约束: 1、题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。 2、允许题目编号有缺失,例如:所有输入的题号为1、2、5,缺少其中的3号题。此种情况视为正常。 样例:#N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 2、试卷信息 试卷信息为独行输入,一行为一张试卷,多张卷可分多行输入数据。 格式:"#T:"+试卷号+" "+题目编号+"-"+题目分值+" "+题目编号+"-"+题目分值+... 格式约束: 题目编号应与题目信息中的编号对应。 一行信息中可有多项题目编号与分值。 样例:#T:1 3-5 4-8 5-2 3、学生信息 学生信息只输入一行,一行中包括所有学生的信息,每个学生的信息包括学号和姓名,格式如下。 格式:"#X:"+学号+" "+姓名+"-"+学号+" "+姓名....+"-"+学号+" "+姓名 格式约束: 答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。 样例: #S:1 #A:5 #A:22 1是试卷号 5是1号试卷的顺序第1题的题目答案 4、答卷信息 答卷信息按行输入,每一行为一张答卷的答案,每组答案包含某个试卷信息中的题目的解题答案,答案的顺序号与试 卷信息中的题目顺序相对应。答卷中: 格式:"#S:"+试卷号+" "+学号+" "+"#A:"+试卷题目的顺序号+"-"+答案内容+... 格式约束: 答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。 答案内容可以为空,即””。 答案内容中如果首尾有多余的空格,应去除后再进行判断。 样例: #T:1 1-5 3-2 2-5 6-9 4-10 7-3 #S:1 20201103 #A:2-5 #A:6-4 1是试卷号 20201103是学号 2-5中的2是试卷中顺序号,5是试卷第2题的答案,即T中3-2的答案 6-4中的6是试卷中顺序号,4是试卷第6题的答案,即T中7-3的答案 注意:不要混淆顺序号与题号 5、删除题目信息 删除题目信息为独行输入,每一行为一条删除信息,多条删除信息可分多行输入。该信息用于删除一道题目信息,题目被删除之后,引用该题目的试卷依然有效,但被删除的题目将以0分计,同时在输出答案时,题目内容与答案改为一条失效提示,例如:”the question 2 invalid~0” 格式:"#D:N-"+题目号 格式约束: 题目号与第一项”题目信息”中的题号相对应,不是试卷中的题目顺序号。 本题暂不考虑删除的题号不存在的情况。 样例: #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-5 2-8 #X:20201103 Tom-20201104 Jack #S:1 20201103 #A:1-5 #A:2-4 #D:N-2 end 输出 alert: full score of test paper1 is not 100 points 1+1=~5~false the question 2 invalid~0 20201103 Tom: 0 0~0 答题信息以一行"end"标记结束,"end"之后的信息忽略。 输出格式: 1、试卷总分警示 该部分仅当一张试卷的总分分值不等于100分时作提示之用,试卷依然属于正常试卷,可用于后面的答题。如果总分等于100 分,该部分忽略,不输出。 格式:"alert: full score of test paper"+试卷号+" is not 100 points" 样例:alert: full score of test paper2 is not 100 points 2、答卷信息 一行为一道题的答题信息,根据试卷的题目的数量输出多行数据。 格式:题目内容+"~"+答案++"~"+判题结果(true/false) 约束:如果输入的答案信息少于试卷的题目数量,每一个缺失答案的题目都要输出"answer is null" 。 样例: 3+2=~5~true 4+6=~22~false. answer is null 3、判分信息 判分信息为一行数据,是一条答题记录所对应试卷的每道小题的计分以及总分,计分输出的先后顺序与题目题号相对应。 格式:**学号+" "+姓名+": "**+题目得分+" "+....+题目得分+"~"+总分 格式约束: 1、没有输入答案的题目、被删除的题目、答案错误的题目计0分 2、判题信息的顺序与输入答题信息中的顺序相同 样例:20201103 Tom: 0 0~0 根据输入的答卷的数量以上2、3项答卷信息与判分信息将重复输出。 4、被删除的题目提示信息 当某题目被试卷引用,同时被删除时,答案中输出提示信息。样例见第5种输入信息“删除题目信息”。 5、题目引用错误提示信息 试卷错误地引用了一道不存在题号的试题,在输出学生答案时,提示”non-existent question~”加答案。例如: 输入: #N:1 #Q:1+1= #A:2 #T:1 3-8 #X:20201103 Tom-20201104 Jack-20201105 Www #S:1 20201103 #A:1-4 end 输出: alert: full score of test paper1 is not 100 points non-existent question~0 20201103 Tom: 0~0 如果答案输出时,一道题目同时出现答案不存在、引用错误题号、题目被删除,只提示一种信息,答案不存在的优先级最高,例如: 输入: #N:1 #Q:1+1= #A:2 #T:1 3-8 #X:20201103 Tom-20201104 Jack-20201105 Www #S:1 20201103 end 输出: alert: full score of test paper1 is not 100 points answer is null 20201103 Tom: 0~0 6、格式错误提示信息 输入信息只要不符合格式要求,均输出”wrong format:”+信息内容。 例如:wrong format:2 #Q:2+2= #4 7、试卷号引用错误提示输出 如果答卷信息中试卷的编号找不到,则输出”the test paper number does not exist”,答卷中的答案不用输出,参见样例8。 8、学号引用错误提示信息 如果答卷中的学号信息不在学生列表中,答案照常输出,判分时提示错误。参见样例9。 本题暂不考虑出现多张答卷的信息的情况。 输入样例1: 简单输入,不含删除题目信息。例如: #N:1 #Q:1+1= #A:2 #T:1 1-5 #X:20201103 Tom #S:1 20201103 #A:1-5 end 输出样例1: 在这里给出相应的输出。例如: alert: full score of test paper1 is not 100 points 1+1=~5~false 20201103 Tom: 0~0 输入样例2: 简单输入,答卷中含多余题目信息(忽略不计)。例如: #N:1 #Q:1+1= #A:2 #T:1 1-5 #X:20201103 Tom #S:1 20201103 #A:1-2 #A:2-3 end 输出样例3 简单测试,含删除题目信息。例如: alert: full score of test paper1 is not 100 points 1+1=~2~true 20201103 Tom: 5~5 输入样例3: 简单测试,含删除题目信息。例如: #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-5 2-8 #X:20201103 Tom-20201104 Jack-20201105 Www #S:1 20201103 #A:1-5 #A:2-4 #D:N-2 end 输出样例3: 在这里给出相应的输出,第二题由于被删除,输出题目失效提示。例如: alert: full score of test paper1 is not 100 points 1+1=~5~false the question 2 invalid~0 20201103 Tom: 0 0~0 输入样例4: 简单测试,含试卷无效题目的引用信息以及删除题目信息(由于题目本身无效,忽略)。例如: #N:1 #Q:1+1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-5 3-8 #X:20201103 Tom-20201104 Jack-20201105 Www #S:1 20201103 #A:1-5 #A:2-4 #D:N-2 end 输出样例4: 输出不存在的题目提示信息。例如: alert: full score of test paper1 is not 100 points 1+1=~5~false non-existent question~0 20201103 Tom: 0 0~0 输入样例5: 综合测试,含错误格式输入、有效删除以及无效题目引用信息。例如: #N:1 +1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-5 2-8 #X:20201103 Tom-20201104 Jack-20201105 Www #S:1 20201103 #A:1-5 #A:2-4 #D:N-2 end 输出样例5: 在这里给出相应的输出。例如: wrong format:#N:1 +1= #A:2 alert: full score of test paper1 is not 100 points non-existent question~0 the question 2 invalid~0 20201103 Tom: 0 0~0 输入样例6: 综合测试,含错误格式输入、有效删除、无效题目引用信息以及答案没有输入的情况。例如: #N:1 +1= #A:2 #N:2 #Q:2+2= #A:4 #T:1 1-5 2-8 #X:20201103 Tom-20201104 Jack-20201105 Www #S:1 20201103 #A:1-5 #D:N-2 end 输出样例6: 答案没有输入的优先级最高。例如: wrong format:#N:1 +1= #A:2 alert: full score of test paper1 is not 100 points non-existent question~0 answer is null 20201103 Tom: 0 0~0 输入样例7: 综合测试,正常输入,含删除信息。例如: #N:2 #Q:2+2= #A:4 #N:1 #Q:1+1= #A:2 #T:1 1-5 2-8 #X:20201103 Tom-20201104 Jack-20201105 Www #S:1 20201103 #A:2-4 #A:1-5 #D:N-2 end 输出样例7: 例如: alert: full score of test paper1 is not 100 points 1+1=~5~false the question 2 invalid~0 20201103 Tom: 0 0~0 输入样例8: 综合测试,无效的试卷引用。例如: #N:1 #Q:1+1= #A:2 #T:1 1-5 #X:20201103 Tom #S:2 20201103 #A:1-5 #A:2-4 end 输出样例8: 例如: alert: full score of test paper1 is not 100 points The test paper number does not exist 输入样例9: 无效的学号引用。例如: #N:1 #Q:1+1= #A:2 #T:1 1-5 #X:20201106 Tom #S:1 20201103 #A:1-5 #A:2-4 end 输出样例9: 答案照常输出,判分时提示错误。例如: alert: full score of test paper1 is not 100 points 1+1=~5~false 20201103 not found 输入样例10: 信息可打乱顺序输入:序号不是按大小排列,各类信息交错输入。但本题不考虑引用的题目在被引用的信息之后出现的情况(如试卷引用的所有题目应该在试卷信息之前输入),所有引用的数据应该在被引用的信息之前给出。例如: #N:3 #Q:中国第一颗原子弹的爆炸时间 #A:1964.10.16 #N:1 #Q:1+1= #A:2 #X:20201103 Tom-20201104 Jack-20201105 Www #T:1 1-5 3-8 #N:2 #Q:2+2= #A:4 #S:1 20201103 #A:1-5 #A:2-4 end 输出样例10: 答案按试卷中的题目顺序输出。例如: alert: full score of test paper1 is not 100 points 1+1=~5~false 中国第一颗原子弹的爆炸时间~4~false 20201103 Tom: 0 0~0
设计
程序3在2的基础上增加了对学生信息的处理,同样使用Map将学生信息与答卷关联起来。并且对试卷类又增加了一个删题的功能,
(1)试卷类:创建一个deleteQuestion方法实现对输入删题信息的处理,以及对错误地引用了不存在题号的试题的处理
class TestPaper {
private int id; //试卷序号 第一张
private LinkedHashMap<Integer, Integer> questionsAndScores;//第一张试卷的题目序号和得分
//有参构造
public TestPaper(int id) {
this.id = id;
this.questionsAndScores = new LinkedHashMap<>();
}
//get set
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
//添加一道题目
public void addQuestion(int questionId, int score) {
questionsAndScores.put(questionId, score);
}
public LinkedHashMap<Integer, Integer> getQuestionsAndScores() {
return questionsAndScores;
}
//获取这个试卷中第i题的得分
public int oneScore(int i) {
return questionsAndScores.getOrDefault(i, 0); // 如果题目不存在,返回0
}
//判断该试卷中总分是否为100
public void ifTotalScore() {
int total = 0;
for (int score : questionsAndScores.values()) {
total += score;
}
if(total!=100){
System.out.printf("alert: full score of test paper%d is not 100 points\n",getId());
}
}
//删题
public void deleteQuestion(int questionId) {
questionsAndScores.remove(questionId);
}
//试卷错误地引用了不存在题号的试题
public boolean ifExisQues(Map<Integer, Question> questionsById) {
for (int questionId : questionsAndScores.keySet()) {
if (!questionsById.containsKey(questionId)) {
return false;
}
}
return true;
}
}
(2)学生类:该类用于实现对输入学生信息的储存
class Student{ private String studentId; private String name; public Student(String studentId, String name) { this.studentId = studentId; this.name = name; } public String getStudentId() { return studentId; } public void setStudentId(String studentId) { this.studentId = studentId; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
(3)答卷类:在2的基础上使用Map将学生的信息和答卷相关联
class AnswerSheet { private int testPaperId; private String num;//学生学号 private List<String> answers; //参数构造 public AnswerSheet(int testPaperId, String num, List<String> answers) { this.testPaperId = testPaperId; this.num = num; this.answers = new ArrayList<>(); } //get set public String getNum() { return num; } public void setNum(String num) { this.num = num; } public void addAnswer(String answer) { answers.add(answer); } public int getTestPaperId() { return testPaperId; } public void setTestPaperId(int testPaperId) { this.testPaperId = testPaperId; } public void setAnswers(List<String> answers) { this.answers = answers; } //得到一个包含所有答案的字符串数组。 public String[] getAnswers() { return answers.toArray(new String[0]); } public boolean[] checkAnswers(Map<Integer, Question> questionsById) { boolean[] results = new boolean[answers.size()]; int index = 0; for (int questionId : questionsById.keySet()) { if (index < answers.size() && answers.get(index) != null) { Question question = questionsById.get(questionId); results[index] = question.getAnswer().equals(answers.get(index)); } else { results[index] = false; //没有答案或者超出范围 } index++; } return results; } //把学号与map学生中的姓名匹配上 public String isMapname(Map<Integer, Student> students, String num) { int id = Integer.parseInt(num); // 从Map中查找学生 Student student = students.get(id); // 如果找到学生,返回其姓名;否则返回null return student != null ? student.getName() : null; } }
(4)解析字符串类:该类的matchString现还需实现对学生信息的字符串和删题信息的字符串处理,为实现输入不合法格式的判断,我设计了ifvaild方法来判断格式的正确与错误
//用于对输入字符串进行解析 class RegexParser { Map<Integer, Question> questionsById = new LinkedHashMap<>(); Map<Integer, TestPaper> testPapersById = new HashMap<>(); Map<Integer, AnswerSheet> answerSheetsById = new HashMap<>(); Map<Integer, Student> Students = new HashMap<>(); //构造 public RegexParser() { } public RegexParser(Map<Integer, Question> questionsById, Map<Integer, TestPaper> testPapersById, Map<Integer, AnswerSheet> answerSheetsById, Map<Integer, Student> students) { this.questionsById = questionsById; this.testPapersById = testPapersById; this.answerSheetsById = answerSheetsById; Students = students; } //get set public Map<Integer, Question> getQuestionsById() { return questionsById; } public void setQuestionsById(Map<Integer, Question> questionsById) { this.questionsById = questionsById; } public Map<Integer, TestPaper> getTestPapersById() { return testPapersById; } public void setTestPapersById(Map<Integer, TestPaper> testPapersById) { this.testPapersById = testPapersById; } public Map<Integer, AnswerSheet> getAnswerSheetsById() { return answerSheetsById; } public void setAnswerSheetsById(Map<Integer, AnswerSheet> answerSheetsById) { this.answerSheetsById = answerSheetsById; } public Map<Integer, Student> getStudents() { return Students; } public void setStudents(Map<Integer, Student> students) { Students = students; } //判断是否合法 public boolean ifvaild(String line) { //匹配题目信息格式 if (Pattern.compile("#N:(\\d+) #Q:(.*?) #A:(\\d+)").matcher(line).matches()) { return true; } // 匹配试卷信息格式 else if (Pattern.compile("#T:(\\d+) (.+)").matcher(line).matches()) { return true; } // 匹配答卷信息格式 else if (Pattern.compile("#S:(\\d+) (\\d+) (.+)").matcher(line).matches()) { return true; } // 匹配学生信息格式 else if (Pattern.compile("#X:(\\d+) (.+)").matcher(line).matches()) { return true; } // 匹配删除题目信息格式 else if (Pattern.compile("^#D:N-\\d+$").matcher(line).matches()) { return true; } // 如果都不匹配,则返回false return false; } //匹配字符串(不合法时输出错误) public void matchString(String line) { if (ifvaild(line)) { //输入题目信息 if (line.startsWith("#N:")) { if (testPapersById.isEmpty() && answerSheetsById.isEmpty()) { String[] parts = line.split(" "); int id = 0; String content = null; String answer = null; for (String part : parts) { if (part.startsWith("#N:")) { String[] idParts = part.split(":"); id = Integer.parseInt(idParts[1].trim()); } else if (part.startsWith("#Q:")) { content = part.substring("#Q:".length()).trim(); } else if (part.startsWith("#A:")) { answer = part.substring("#A:".length()).trim(); } } questionsById.put(id, new Question(id, content, answer)); } } //输入试卷信息 else if (line.startsWith("#T:")) { String[] parts = line.substring("#T:".length()).trim().split(" "); int id = Integer.parseInt(parts[0]);//第几张试卷 TestPaper testPaper = new TestPaper(id); for (int i = 1; i < parts.length; i++) { String[] qScoreParts = parts[i].split("-"); int questionId = Integer.parseInt(qScoreParts[0]); int score = Integer.parseInt(qScoreParts[1]); testPaper.addQuestion(questionId, score); testPapersById.put(id, testPaper); } } //输入学生信息 else if (line.startsWith("#X:")) { String[] parts = line.substring("#X:".length()).trim().split("-"); for (String part : parts) { String[] studentInfo = part.trim().split("\\s+"); // 使用空白字符分割id和name if (studentInfo.length == 2) { // 确保id和name都存在 int studentId = Integer.parseInt(studentInfo[0]); // 假设id是整数 String name = studentInfo[1]; Student student = new Student(String.valueOf(studentId), name); // 使用String.valueOf确保id是String类型 Students.put(studentId, student); // 将Student对象添加到Map中 } } } //输入答卷信息 else if (line.startsWith("#S:")) { String[] parts = line.substring("#S:".length()).trim().split(" "); int id = Integer.parseInt(parts[0]); String studentId = parts[1]; AnswerSheet answersheet = new AnswerSheet(id, studentId, null);//???? for (int i = 2; i < parts.length; i++) { if (parts[i].startsWith("#A:")) { String[] questIdScore = parts[i].substring("#A:".length()).trim().split("-"); String answer = questIdScore[1]; answersheet.addAnswer(answer); } } answerSheetsById.put(id, answersheet); } //输入删除题目信息 else if (line.startsWith("#D:N-")) { Pattern pattern = Pattern.compile("#D:N-(\\d+)"); Matcher matcher = pattern.matcher(line); if (matcher.find()) { String numberStr = matcher.group(1); int number = Integer.parseInt(numberStr); int invalid = number; TestPaper test = testPapersById.get(1); test.deleteQuestion(number); } } else { System.out.println("wrong format:" + line); } } } }
(5)Main:在2的基础上要实现一些提示信息,如被删除的题目提示信息,题目引用错误提示信息,格式错误提示信息,试卷号引用错误提示输出,学号引用错误提示信息等。(目前还有试卷号引用错误提示输出,学号引用错误提示信息未完善)
public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); String line; int invalid=0; RegexParser regexParser = new RegexParser(); while (!(line = input.nextLine()).equals("end")) { regexParser.matchString(line); } Map<Integer,Question>questionsById = regexParser.getQuestionsById(); Map<Integer, TestPaper> testPapersById = regexParser.getTestPapersById(); Map<Integer, AnswerSheet> answerSheetsById = regexParser.getAnswerSheetsById(); Map<Integer, Student> Students = regexParser.getStudents(); TestPaper test= testPapersById.get(1);//test 是第一张试卷 test.ifTotalScore(); AnswerSheet answersheet=answerSheetsById.get(1); String[] answer =answersheet.getAnswers(); boolean[] results=answersheet.checkAnswers(questionsById); int total=0,i=0; LinkedHashMap<Integer, Integer> questionsAndScores = test.getQuestionsAndScores(); for(int questionId:questionsAndScores.keySet()) { Question question = questionsById.get(questionId); if (question != null) { String content = question.getContent(); i++; if(content!=null){ System.out.println(content+"~"+answer[i-1]+"~"+results[i-1]); } } } if(test.ifExisQues(questionsById)==false){ System.out.println("non-existent question~0"); } else { if (invalid != 0) { System.out.println("the question " + invalid + " invalid~0"); } } System.out.printf("%s %s:",answersheet.getNum(),answersheet.isMapname(Students,answersheet.getNum())); for(i=1;i<=questionsById.size();i++){ if(results[i-1]){ total =total+test.oneScore(i); System.out.printf(" %d",test.oneScore(i)); } else{ System.out.printf(" 0"); } } System.out.printf("~%d",total); }
我的类图
分析结果
3.踩坑心得
(1)在写答题判题程序3的时候由于我的1和2以及使用了很多Map数据结构将题目,试卷中每题分数,答卷等等关联起来,导致写3的时候添加的学生类也使用Map去关联这些信息的时候让我有点理不清楚这些结构之间的关系,调用的时候经常出现错误,写的时候应该多多注释清楚他们之间的逻辑
(2)对数组的使用分不清i从几开始造成空指针异常或者数组越界
用debug进行调试的时候应仔细注意
(3)输出Map中的键值时,他并不会按照输入顺序进行输出,而且会按照顺序输出,这就导致乱序输入题目信息时不能按照输入顺序正确处理信息,
使用LinkedHashMap可以按照插入的顺序进行输出
4.改进建议
根据题目的要求,由于在输入的时候过于繁琐,我应该新建一个类用于输出信息。在输入提示信息的时候,我应该使用根据严密的逻辑来进行判断输出什么信息。对正则表达式匹配字符串的运用应该掌握的更加熟练。
5.总结
正则表达式在处理和解析复杂的文本输入时有很大的优势。通过精确定义要匹配的模式,可以从中提取出有用的信息,极大地简化了输入处理的逻辑。数据结构设计要合理,写这种复杂的程序,切记不可看完题目直接来敲代码,设计需求是重中之重,通过设计合理的数据结构来存储题目、试卷、学生和答案等信息。例如,使用HashMap来存储题目信息,LinkHashMap来储存试卷信息
标签:题目,String,答题,试卷,样例,OOP,判题,public,输入 From: https://www.cnblogs.com/lhf1/p/18148356