239
社区成员




这个作业属于哪个课程 | 软件工程实践-2024学年-W班 |
---|---|
这个作业要求在哪里 | 软件工程实践第二次作业——个人实战 |
这个作业的目标 | 收集数据、编写代码、单元测试、性能改进、异常处理、撰写博客 |
其他参考文献 | 单元测试与回归测试、源代码管理、工程师的能力评估和发展 |
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 100 | 150 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 600 | 800 |
• Design Spec | • 生成设计文档 | 100 | 120 |
• Design Review | • 设计复审 | 20 | 20 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 50 | 50 |
• Design | • 具体设计 | 60 | 80 |
• Coding | • 具体编码 | 600 | 720 |
• Code Review | • 代码复审 | 30 | 30 |
• Test | • 测试(自我测试,修改代码,提交修改) | 120 | 150 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 60 | 50 |
• Size Measurement | • 计算工作量 | 20 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 90 | 100 |
合计 | 1850 | 2290 |
在官方网站打开开发者工具,找到以下两份可格式化的json文件。
解析后替换为更利于文件流读取与分析的txt文件。
4. Nationtotal.txt 用于实现功能1
5. ResultDate.txt 用于实现功能2
6. NationInfo.txt 用于实现功能3
rank1:string
gold:number
silver:number
bronze:number
total:number
-----
winner:string
nationality:string
gold:number
silver:number
bronze:number
total:number
-----
stringVSstring string
gold:number/number
silver:number/number
bronze:number/number
total:number/number
-----
4.出现无法识别的指令,则输出Error;
result后的日期不在限定时间内(7月27日-8月11日),则输出N/A。
Java -jar OlympicSearch.jar input.txt output.txt
src
│ OlympicSearch.java # 主程序所在模块,读命令,调用函数
public static List<String> readCommands(String input):读取文件中的命令
public static void main(String[] args) : 入口函数,调用不同的功能函数
│ NationTotal.java # 响应total指令
public static List<CountryMedal> readMedalData(String filePath) :获取NationTotal文件中的国家奖牌数据,转换成对象
public static List<CountryMedal> sortByMedals(List<CountryMedal> countries) :按奖牌数量逐级降序排列
public static void writeOutput(List<CountryMedal> countries, BufferedWriter writer) : 获得最终的数据并写入output
│ ResultDate.java # 响应result date指令
public static Map<String, List<WinnerMedal>>readAllResults(String filePath):// 读取ResultDate文件,获得所有对象,按日期分组
public static void ResultCommand(String date, String ResultDateFilePath, BufferedWriter writer) : // 处理主函数传来的ResultDate指令
public static List<WinnerMedal> sortByMedals(List<WinnerMedal> winners):按奖牌数量逐级降序排列
public static void writeOutput(List<WinnerMedal> winners, BufferedWriter writer): 获得最终的数据并写入output
│ NationPK.java # 响应pk指令
public static Map<String, Map<String, int[]>> readCountryPKData(String filePath): // 读取 NationInfo 文件,返回 <比赛项目,<国家,奖牌数[]>> 的数据结构,便于储存与比较
public static void CountryPK(String country1, String country2, String project, String filePath, BufferedWriter writer) : 获取并PK两个国家在某个项目上的奖牌数,写入output
├─entity
CountryMedal .java # 国家-奖牌类
WinnerMedal.java # 运动员-奖牌类
try {
// 读取指令
List<String> commands = readCommands(inputFilePath);
// 创建BufferedWriter,写入的内容将覆盖原output.txt
try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath,false))) {
for (String command : commands) {
// 去除单行首尾空格
command = command.trim();
// 若指令可以满足NationTotal的1个参数
if ("total".equalsIgnoreCase(command)) {
//调用NationTotal
List<CountryMedal> countries = NationTotal.readMedalData(NationTotalFilePath);
List<CountryMedal> sortedCountries = NationTotal.sortByMedals(countries);
NationTotal.writeOutput(sortedCountries, writer);
}
// 若指令可以满足ResultDate的2个参数
else if (command.startsWith("result")) {
//根据空格数拆分part
String[] parts = command.split("\\s+");
if (parts.length >= 2) {
String date = parts[1].trim();
// 调用ResultDate
ResultDate.processResultCommand(date, ResultDateFilePath, writer);
// 处理错误参数
} else {
writer.write("Error\n");
writer.write("-----\n");
}
// 若指令可以满足NationPK的4个参数
} else if (command.startsWith("PK")) {
//根据空格数拆分part
String[] parts = command.split("\\s+");
if (parts.length >= 4) {
String country1 = parts[1].trim();
String country2 = parts[2].trim();
String project = parts[3].trim();
NationPK.processComparison(country1, country2, project, countryVsFilePath, writer);
} else {
writer.write("Error\n");
writer.write("-----\n");
}
// 错误指令
} else {
writer.write("Error\n");
writer.write("-----\n");
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
public static void processResultCommand(String date, String ResultDateFilePath, BufferedWriter writer) {
try {
// 读取所有对象及其对应日期
Map<String, List<WinnerMedal>> allResults = readAllResults(ResultDateFilePath);
// 转换日期格式,如 "0809" => "8.9"
String month = date.substring(0, 2).replaceFirst("^0+", "");
String day = date.substring(2, 4).replaceFirst("^0+", "");
String formattedDate = month + "." + day;
// 获取指定日期的对象
if (allResults.containsKey(formattedDate)) {
List<WinnerMedal> winners = allResults.get(formattedDate);
// 根据金牌、银牌、铜牌排序
List<WinnerMedal> sortedWinners = sortByMedals(winners);
// 输出结果到 BufferedWriter
writeOutput(sortedWinners, writer);
} else {
// writer.write("未找到指定日期的比赛结果:" + formattedDate + "\n");
writer.write("N/A" + "\n");
writer.write("-----\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 按照运动员获得的奖牌数量,逐级降序排列
public static List<WinnerMedal> sortByMedals(List<WinnerMedal> winners) {
return winners.stream()
.sorted(Comparator.comparingInt((WinnerMedal w) -> w.gold)
.thenComparingInt(w -> w.silver)
.thenComparingInt(w -> w.bronze).reversed())
.collect(Collectors.toList());
}
public static void CountryPK(String country1, String country2, String project, String NationInfoFilePath, BufferedWriter writer) {
try {
// 读取 NationInfo 文件
Map<String, Map<String, int[]>> NationInfoData = readCountryPKData(NationInfoFilePath);
if (NationInfoData.containsKey(project)) { //包含指令要求的项目
Map<String, int[]> countryData = NationInfoData.get(project);
// 在该项目中寻找这两个国家的奖牌数据,如果没有则默认奖牌数为0
int[] country1Data = countryData.getOrDefault(country1, new int[]{0, 0, 0, 0});
int[] country2Data = countryData.getOrDefault(country2, new int[]{0, 0, 0, 0});
// 将PK结果写入 BufferedWriter
writer.write(country1 + " VS " + country2 + " " + project + "\n");
writer.write("gold: " + country1Data[0] + " / " + country2Data[0] + "\n");
writer.write("silver: " + country1Data[1] + " / " + country2Data[1] + "\n");
writer.write("bronze: " + country1Data[2] + " / " + country2Data[2] + "\n");
writer.write("total: " + country1Data[3] + " / " + country2Data[3] + "\n");
writer.write("-----\n");
} else {
writer.write("N/A" + "\n");
writer.write("-----\n");
}
} catch (IOException e) {
try {
//writer.write("Error occurred during comparison\n");
writer.write("Error\n");
writer.write("-----\n");
} catch (IOException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
使用StringBuffer拼接字符串不会产生新对象,减少频繁创建对象,配合BufferedWriter的缓冲功能,可以提高读写效率。
当读取一组关系对象时可存入HashMap中,可以减少读取数据文件的时间。
将input中的内容一次性读取出来用List<String>存储,可以减少读取指令文件的时间。
以下包含10+各测试用例。
功能1测试(input-output)
功能2测试(input-output)
功能3测试(input-output)
读取指令可能存在异常,因此使用try-catch语句来包含文件读取语句。
当指令参数异常时,应作业要求,不选择try报错处理,而是在output文件中打印“Error”字样。
当日期出现非法输入时,应作业要求,不选择try报错处理,而是在output文件中打印“N/A”字样。
PK功能可能出现的异常状态很多,根据类型选用"Error"或"N/A"或e.printStackTrace()反馈异常。
这次实践涉及的技术多且杂,包括网站数据的爬取与解析,文件格式化与读写,数据的存储与处理,异常处理,单元测试,Git的使用等等,会遇到很多很多的感到一时无语,束手无策的困难。但在同学的帮助与自己的查找下解决了大部分的困难。经过这次实践,我更深刻地理解到了软件工程领域的浩瀚无边,大一大二时所忙碌的编写代码环节,在一个工程中所耗费的时间与精力竟然也并非重点。查找与学习永无止境。
你的博客文档标题的跳转地址似乎错了