• 全部
...

软件工程实践第二次作业——个人实战

221900136_黄依灵 学生 2022-03-05 12:01:35
这个作业属于哪个课程2022年福大-软件工程、实践-W班
这个作业要求在哪里软件工程实践第二次作业——个人实战
这个作业的目标完成对冬奥会的赛事数据的收集,并实现一个能够对国家排名及奖牌个数统计的控制台程序
其他参考文献百度

目录

  • 一. Gitcode项目地址
  • 二. PSP表格
  • 三. 解题思路
  • 1.数据爬取
  • 2.文件读取
  • 3.解析json数据
  • 3.指令正确性判断
  • 四.接口设计和实现过程
  • 五.关键代码展示
  • 六.性能改进
  • 七.单元测试
  • 八.异常处理
  • 九.心得体会


一. Gitcode项目地址

仓库地址请点击这里


二. PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2023
• Estimate• 估计这个任务需要多少时间2023
Development开发9851180
• Analysis• 需求分析 (包括学习新技术)40100
• Design Spec• 生成设计文档3020
• Design Review• 设计复审1010
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)1520
• Design• 具体设计6070
• Coding• 具体编码600470
• Code Review• 代码复审3070
• Test• 测试(自我测试,修改代码,提交修改)200420
Reporting报告80115
• Test Report• 测试报告3030
• Size Measurement• 计算工作量2035
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划3050
合计10851318

三. 解题思路

1.数据爬取

在这里插入图片描述

  • 进入冬奥官网 选择赛程,按键F12并刷新出现上图所示,点击网络,选择所需要的数据在新标签页打开,下载到本地data文件夹,这样更方便对于数据的读取和处理

  • 保存下来后,用vscode对文件格式化,对头尾进行处理,再用在线网站进行unicode中文转换,将转换后数据中的 " % / "全部替换为" / "

2.文件读取

使用BuilderReader.readLine()和BuilderWriter对文件进行读写,使用StringBuilder.append()将读取到的文件内容拼接

3.解析json数据

引入第三方库Gson,根据data文件夹中json数据的特点,因BufferReader.readLine()可读取一行的信息,所以当读取到" }," 或者 " }" 时,就可以用gson提取一个对象

Matches match = gson.fromJson(builder.toString(), Matches.class);

3.指令正确性判断

  1. 首先先判断是否为空行,若是空行跳过
  2. 判断是否为total,是的话则输出奖牌总榜
  3. 判断是否为schedule开头的字符串,若schedule后的日期并不在冬奥期间,则输出N/A,若指令正确,则 输出每日赛程
  4. 其余指令输出Error

四.接口设计和实现过程

共有四个类:OlympicSearch、Lib、Medals、Matches。

  • 其中Medals和Matches用于Gson匹配json数据,提取所需要的json数据
  • Lib用于处理total指令和schedule指令的输出,在Lib中读取json文件并通过Gson创建Medals和Matches对象,最终调用Medals.toString()和Matches.toString()将字符串存储在属性中,方便重复调用
  • OlympicSearch存放main函数,从cmd中读取input.txt和output.txt路径,并对指令正确性进行判断,可调用Lib类

OlympicSearch类

  1. public class OlympicSearch
  2. {
  3. //br用于读input.txt文件,bw用于写output.txt文件
  4. BufferedReader br;
  5. BufferedWriter bw;
  6. //根据传入的路径初始化 br和 bw
  7. public OlympicSearch(String input,String output)
  8. //获取 br
  9. public BufferedReader getBufferReader()
  10. //获取 bw
  11. public BufferedWriter getBufferWriter()
  12. //判断日期是否正确,传入的参数为用空格分割指令得到的字符串数组
  13. public static boolean rightDate(String[] strs)
  14. public static void main(String[] args)
  15. }

Lib类

  1. public class Lib
  2. {
  3. //用于存储对应输出结果的字符串,以便重复指令可直接输出该字符串,提高性能
  4. String medalsString;
  5. Map<String,String> matchesString;
  6. BufferedWriter bw;
  7. //获取OlympicSearch的属性bw
  8. Lib(BufferedWriter bw)
  9. //读取date.json文件,将该日期下输出结果的字符串存储在matchesString中
  10. void initMatches(String date) throws IOException
  11. //读取total.json文件,将输出结果的字符串存储在medalsString中
  12. void initMeadls() throws IOException
  13. //main方法中在指令正确情况下调用该函数
  14. void printMedals() throws IOException
  15. //main方法中在指令正确情况下调用该函数
  16. void printMatches(String date) throws IOException
  17. }

Medals类(用于Gson匹配json数据)

  1. class Medals
  2. {
  3. String rank;
  4. String countryid;
  5. String gold;
  6. String silver;
  7. String bronze;
  8. String count;
  9. //对得到的json数据进行处理
  10. public void changeData()
  11. //转换成字符串
  12. public String toString()
  13. }

Matches类(用于Gson匹配json数据)

  1. class Matches
  2. {
  3. String startdatecn;
  4. String itemcodename;
  5. String title;
  6. String homename, awayname;
  7. String venuename;
  8. //对得到的json数据进行处理
  9. public void changeData()
  10. //转换成字符串
  11. public String toString()
  12. }

五.关键代码展示

当输入指令为 schedule 0202 时,main函数会调用 Lib.printMatches("0202") 方法

  1. 首先通过 matchesString.containsKey("0202") 判断是否以前存储过0202这一天的输出内容
  2. 若无就需要调用 Lib.initMatches("0202") 从 0202.json 文件中提取数据并转换为字符串形式存储到 matchesString 中
  3. 向 output.txt 中写入 matchesString.get("0202")
  1. void printMatches(String date) throws IOException
  2. {
  3. if(!matchesString.containsKey(date))
  4. {
  5. try
  6. {
  7. initMatches(date);
  8. }
  9. catch (IOException e)
  10. {
  11. System.out.println("读取"+date+".json文件失败");
  12. }
  13. }
  14. bw.write(matchesString.get(date));
  15. bw.flush();
  16. }

此处用到Gson并不是直接对整个json数组解析 ,而是通过 StringBuilder 和 BufferedReader 将json文件划分成一个个对象,转换为一个个Matches类,逐个存储到 List<Matches> matchesList中,最后用for循环将matchesList中的每个Matches通过调用 Matches.toString() 方法得到的字符串拼接在一起,赋值给 matchesString

  1. void initMatches(String date) throws IOException
  2. {
  3. Gson gson = new Gson();
  4. String content = "";
  5. List<Matches> matchesList=new ArrayList<Matches>();
  6. StringBuilder builder = new StringBuilder();
  7. String filePath = "src\\data\\schedule\\" + date + ".json";
  8. File file = new File(filePath);
  9. if (!file.exists()) System.out.println("找不到文件"+date+".json");
  10. InputStreamReader streamReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
  11. BufferedReader bufferedReader = new BufferedReader(streamReader);
  12. //对读取到的json数据划分成单个对象的字符串,再用gson转化成match类
  13. while ((content = bufferedReader.readLine()) != null)
  14. {
  15. if (content.equals(" },") || content.equals(" }"))//根据”...“划分
  16. {
  17. builder.append(" }");
  18. Matches match = gson.fromJson(builder.toString(), Matches.class);
  19. match.changeData();
  20. matchesList.add(match);
  21. builder.delete(0, builder.length());//每提取完一个match类,对builder读取到的内容清空
  22. if (content.equals(" }")) break;
  23. } else builder.append(content);
  24. if (content.equals(" \"matchList\": [")) builder.delete(0, builder.length());//将无效内容删除
  25. }
  26. //将所需的输出内容存储到Map<String,String> matchesString中
  27. builder.delete(0, builder.length());
  28. for (int i=0;i<matchesList.size();i++)
  29. {
  30. builder.append(matchesList.get(i).toString());
  31. if(i<matchesList.size()-1) builder.append("\n");
  32. }
  33. matchesString.put(date,builder.toString());
  34. bufferedReader.close();
  35. streamReader.close();
  36. }

当输入指令为total时,main函数调用Lib.printMedals()方法
相关部分代码与输入指令为 schedule 0202相似,就不再细说

main函数流程图

在这里插入图片描述

六.性能改进

  • 原本的代码
    将数据存储在 Map<String,List<Matches>> matchesMapList<Medals> medalsList 中,每次输出结果都需要用for循环遍历list和map中的每个Medals和Matches,这样对于相同的指令就很浪费时间
  • 性能改进后
    对于total指令,直接将需要输出的字符串存储在 String medalsString中,此后再遇到total指令,直接输出medalsString
    对于schedule 0215指令,将需要输出的字符串存储在Map<String,String> matchesString
    中,键对应日期,值对应指令需要输出的字符串,此后再遇到该指令,直接输出 matchesString.get(date)
    因此,对于重复的指令不需要再读取json文件和存储数据,直接提取字符串

用包含3000条指令的input.txt测试代码运行情况:

改进前:

在这里插入图片描述

改进后:

在这里插入图片描述

性能分析

改进前:

在这里插入图片描述

改进后:

在这里插入图片描述

七.单元测试

以下为部分单元测试代码

  1. @Test
  2. public void testrightDate()
  3. {
  4. search=new OlympicSearch("E:\\软件工程\\OlympicSearch\\input.txt","E:\\软件工程\\OlympicSearch\\output.txt");
  5. lib=new Lib(search.getBufferWriter());
  6. try
  7. {
  8. lib.printMedals();
  9. lib.initMatches("0202");
  10. lib.printMatches("");
  11. lib.initMatches("0201");
  12. }
  13. catch (IOException e)
  14. {
  15. e.printStackTrace();
  16. }
  17. }
  1. @Test
  2. public void testrightDate()
  3. {
  4. assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule","0202"}));
  5. assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule","0220"}));
  6. assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule","0213"}));
  7. assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule"," ","0202"}));
  8. assertEquals(false,OlympicSearch.rightDate(new String[]{"schedule","大时代"}));
  9. assertEquals(false,OlympicSearch.rightDate(new String[]{"schedule","asadsa"}));
  10. }
  11. public void testOlympic()
  12. {
  13. OlympicSearch search1=new OlympicSearch("E:\\软件工程\\OlympicSearch\\input.txt","E:\\软件工程\\OlympicSearch\\output.txt");
  14. OlympicSearch search2=new OlympicSearch("E:\\软件工程\\OlympicSearch\\i1.txt","E:\\软件工程\\OlympicSearch\\t.txt");
  15. }

在这里插入图片描述

八.异常处理

  • 大部分函数都用throws抛出异常,然后在main函数中用 try-catch 处理
  1. void initMeadls() throws IOException
  • 少部分的函数会直接在内部直接用 try-catch 处理
  1. File file = new File("src\\data\\total.json");
  2. if (!file.exists()) System.out.println("找不到文件total.json");
  1. public OlympicSearch(String input,String output)
  2. {
  3. try
  4. {
  5. InputStream in=new FileInputStream(input);
  6. OutputStream out=new FileOutputStream(output);
  7. br=new BufferedReader(new InputStreamReader(in,"UTF-8"));
  8. bw=new BufferedWriter(new OutputStreamWriter(out,"UTF-8"));
  9. }
  10. catch(FileNotFoundException e)
  11. {
  12. System.out.println("文件找不到");
  13. }
  14. catch (UnsupportedEncodingException e)
  15. {
  16. System.out.println("转码异常");
  17. }
  18. }

九.心得体会

  • 这次作业本质上是对文件的读取和写入,写代码的时间不长,但是处理bug的时间很长,印象最深刻的就是在cmd运行程序,明明在idea都能正常运行,但是在cmd运行就会输出结果乱码和报错,百度了一天倒是对idea和cmd的配置有了更多了解,最后没有在百度上找到答案,但是在同学和助教的帮助下解决了bug
  • 还有关于软件的配置问题也要很小心,比起写代码出问题,那些配置出了问题会让人更害怕,其实对于软件的配置还不够了解,在百度的时候很多东西都是懵懵懂懂的
  • 作业一定要提早写,要留充分的时间处理bug,快要到ddl的时候处理bug的状态会差很多
  • 软件开发的时候,沟通了解需求是非常有必要的。比如在群里,大家向助教询问各种指令对应的输出情况后,才对需求有了更深入的了解。同时,需求是容易变动的,所以在编写代码的过程中,对函数要做好划分,这样才能在需求变动的情况下方便代码的变更
  • 这次作业由于对java的知识都忘得差不多了,所以花了很多时间在查询资料上,还是要勤加编写代码
...全文
给本帖投票
365 2 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jingbin-Wang 教师 2022-03-05
  • 打赏
  • 举报
回复
3000条指令的测试效果上看改进的效果不是很明显,如果再上两个数量级看看改进的效果会不会明显一点?
221900136_黄依灵 学生 2022-03-06
  • 举报
回复
@Jingbin-Wang 用11万条指令测试:改进前 程序运行时间: 125358ms 改进后 程序运行时间: 8054ms
内容评分
5星
100%
4星
0%
3星
0%
2星
0%
1星
0%

142

社区成员

发帖
与我相关
我的任务
社区描述
2022年福大-软件工程;软件工程实践-W班
软件工程 高校
社区管理员
  • FZU_SE_teacherW
  • 丝雨_xrc
  • Lyu-
加入社区
社区公告
暂无公告

试试用AI创作助手写篇文章吧

手机看
关注公众号

关注公众号

客服 返回
顶部