111
社区成员




这个作业属于哪个课程 | https://bbs.csdn.net/forums/2401_CS_SE_FZU |
---|---|
这个作业要求在哪里 | https://bbs.csdn.net/topics/619294904 |
这个作业的目标 | 爬虫、理解并学会单元测试、输出2024巴黎奥运会的奖牌总榜以及每日赛程 |
其他参考文献 | 无 |
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 800 | 900 |
• Analysis | • 需求分析 (包括学习新技术) | 60 | 60 |
• Design Spec | • 生成设计文档 | 40 | 30 |
• Design Review | • 设计复审 | 20 | 20 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
• Design | • 具体设计 | 120 | 100 |
• Coding | • 具体编码 | 300 | 240 |
• Code Review | • 代码复审 | 60 | 60 |
• Test | • 测试(自我测试,修改代码,提交修改) | 170 | 360 |
Reporting | 报告 | 120 | 200 |
• Test Repor | • 测试报告 | 60 | 120 |
• Size Measurement | • 计算工作量 | 30 | 30 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 50 |
合计 | 940 | 1120 |
题目中涉及到多个功能:读取奖牌数据、输出赛程信息、根据用户输入进行查询,还涉及到网站数据的爬取
对网站数据爬取
查询奖牌榜时,程序需要从文件中读取并输出包含排名和奖牌数量的数据。
查询赛程时,程序需要根据用户输入的日期,输出相应的赛事安排,如果输入的日期超出了范围或没有相应的赛程,程序需要返回 "N/A"。
处理不同的命令,并确保程序的健壮性,用户输入不合法时,程序能正确处理并输出错误信息。
BufferedReader
和BufferedWriter
的使用方法,以确保正确实现读取和写入操作。Integer.parseInt()
方法来解析日期中的月份和日子。为了验证日期是否在奥运会的有效范围内,参考了Java中的字符串处理和日期比较的方法。MedalTally
类来处理奖牌数据,Schedule
类来处理赛程信息,OlympicSearch
作为主类负责处理用户输入。该项目的核心分为三个类:MedalTally
、Schedule
、OlympicSearch
:
rank.txt
文件中的数据,将奖牌榜展示到输出文件中。MedalTally
和 Schedule
的方法,输出用户所需的数据。OlympicSearch
类是项目的入口,它根据输入的命令行参数,调用 MedalTally
和 Schedule
来执行相应的功能。MedalTally
和 Schedule
类主要是提供数据处理和文件读取的功能。outputMedalTally(String outputFilePath)函数:读取文件,根据不同的内容(如“rank”、“gold”等关键词)判断并输出对应的数据。
为了防止文件被覆盖,每次写入前会清空之前的输出文件内容。采用流式处理机制,保证了文件读取与写入的效率,并且根据关键词识别和处理不同的奖牌数据。
public class MedalTally {
private static final String MEDAL_TALLY_FILE = "src/data/rank.txt";
public static void outputMedalTally(String outputFilePath) {
try (BufferedReader br = new BufferedReader(new FileReader(MEDAL_TALLY_FILE));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath))) {
String line;
while ((line = br.readLine()) != null) {
if (line.startsWith("rank")) {
// 输出排名信息
bw.write(line);
bw.newLine();
} else if (line.startsWith("gold") || line.startsWith("silver") || line.startsWith("bronze") || line.startsWith("total")) {
// 输出奖牌数据
bw.write(line);
bw.newLine();
} else if (line.equals("-----")) {
// 输出分隔线
bw.write(line);
bw.newLine();
} else {
// 遇到未知格式的行
bw.write("Error");
bw.newLine();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
outputSchedule(String date, String outputFilePath):函数根据日期生成相应的文件名,读取文件后输出对应的赛程信息。如果文件不存在,则输出 N/A
。写入赛程时,会在赛程信息末尾添加一条分隔线 -----
,以区分不同的赛程信息。
确保了在不同查询之间不会覆盖文件内容(通过追加模式 true
打开文件),并且处理了文件不存在的情况,提供了错误提示。
public class Schedule {
private static final String DATA_DIRECTORY = "data/";
public static void outputSchedule(String date, String outputFilePath) {
String fileName = DATA_DIRECTORY + "d" + date + ".txt";
File scheduleFile = new File(fileName);
if (!scheduleFile.exists()) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath, true))) {
bw.write("N/A");
bw.newLine();
bw.write("-----");
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
try (BufferedReader br = new BufferedReader(new FileReader(scheduleFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath, true))) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
bw.write("-----");
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
total
命令,则调用 MedalTally
,如果是 schedule
命令,则调用 Schedule
类的相关方法。if (!date.matches("\\d{4}")) {
return false;
}
int month = Integer.parseInt(date.substring(0, 2));
int day = Integer.parseInt(date.substring(2, 4));
return (month == 7 && day >= 24 && day <= 31) || (month == 8 && day >= 1 && day <= 11);
}MedalTally.outputMedalTally()
中,我将文件写入模式改为追加模式 (new FileWriter(outputFilePath, true)
)。Schedule.outputSchedule()
方法中,我优化了文件读取流程,将数据先缓存到内存中,然后再写入输出文件try (BufferedReader br = new BufferedReader(new FileReader(fileName));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath, true))) {
// 读取文件内容到缓存中
StringBuilder content = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
content.append(line).append(System.lineSeparator());
}
// 将缓存内容写入文件
bw.write(content.toString());
bw.write("-----");
bw.newLine();
}
@Test
public static void test1() throws IOException {
String simulatedInput = "total\n";
simulateInput(simulatedInput);
System.out.println("Test 1 Passed: Total Command");
}
@Test
public static void test2() throws IOException {
String simulatedInput = "schedule 0724\n";
simulateInput(simulatedInput);
System.out.println("Test 2 Passed: Schedule Valid Date");
}
@Test
public static void test3() throws IOException {
String simulatedInput = "invalidCommand\n";
simulateInput(simulatedInput);
System.out.println("Test 3 Passed: Invalid Command");
}
@Test
public static void test4() throws IOException {
String simulatedInput = "schedule 9999\n";
simulateInput(simulatedInput);
System.out.println("Test 4 Passed: Schedule Invalid Date");
}
@Test
public static void test5() throws IOException {
String simulatedInput = "total\nschedule 0801\nschedule 1234\n";
simulateInput(simulatedInput);
System.out.println("Test 5 Passed: Multiple Commands");
}
@Test
public static void test6() throws IOException {
String simulatedInput = "total\nschedule 9999\n";
simulateInput(simulatedInput);
System.out.println("Test 6 Passed: Total and Invalid Schedule Commands");
}
@Test
public static void test7() throws IOException {
String simulatedInput = "";
simulateInput(simulatedInput);
System.out.println("Test 7 Passed: Empty File Input");
}
@Test
public static void test8() throws IOException {
String simulatedInput = "total extra_arg\n";
simulateInput(simulatedInput);
System.out.println("Test 8 Passed: Extra Arguments");
}
@Test
public static void test9() throws IOException {
String simulatedInput = "\n";
simulateInput(simulatedInput);
System.out.println("Test 9 Passed: No Commands");
}
@Test
public static void test10() throws IOException {
String simulatedInput = "schedule 0731\n";
simulateInput(simulatedInput);
System.out.println("Test 10 Passed: Schedule Edge Date");
}
IOException
。通过捕获这些异常,确保程序能够处理文件操作错误。public static void outputMedalTally(String outputFilePath) {
try (BufferedReader br = new BufferedReader(new FileReader(MEDAL_TALLY_FILE));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath, true))) {
String line;
while ((line = br.readLine()) != null) {
if (line.startsWith("rank") || line.startsWith("gold") || line.startsWith("silver") ||
line.startsWith("bronze") || line.startsWith("total") || line.equals("-----")) {
bw.write(line);
bw.newLine();
} else {
bw.write("Error");
bw.newLine();
}
}
bw.flush();
} catch (IOException e) {
System.err.println("File I/O error: " + e.getMessage());
// Log error or handle error accordingly
}
}
public static void outputSchedule(String date, String outputFilePath) {
String fileName = DATA_DIRECTORY + "d" + date + ".txt";
File scheduleFile = new File(fileName);
if (!scheduleFile.exists()) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath, true))) {
bw.write("N/A");
bw.newLine();
bw.write("-----");
bw.newLine();
bw.flush();
} catch (IOException e) {
System.err.println("File I/O error: " + e.getMessage());
// Log error or handle error accordingly
}
return;
}
try (BufferedReader br = new BufferedReader(new FileReader(scheduleFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath, true))) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
bw.write("-----");
bw.newLine();
bw.flush();
} catch (IOException e) {
System.err.println("File I/O error: " + e.getMessage());
// Log error or handle error accordingly
}
}