软件工程实践第二次作业——个人实战
| 这个作业属于哪个课程 | 2022年福大-软件工程、实践-W班 |
|---|---|
| 这个作业要求在哪里 | 软件工程实践第二次作业——个人实战 |
| 这个作业的目标 | 完成对冬奥会的赛事数据的收集,并实现一个能够对国家排名及奖牌个数统计的控制台程序 |
| 其他参考文献 | 百度 |
目录
- 一. Gitcode项目地址
- 二. PSP表格
- 三. 解题思路
- 1.数据爬取
- 2.文件读取
- 3.解析json数据
- 3.指令正确性判断
- 四.接口设计和实现过程
- 五.关键代码展示
- 六.性能改进
- 七.单元测试
- 八.异常处理
- 九.心得体会
一. Gitcode项目地址
二. PSP表格
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 23 |
| • Estimate | • 估计这个任务需要多少时间 | 20 | 23 |
| Development | 开发 | 985 | 1180 |
| • Analysis | • 需求分析 (包括学习新技术) | 40 | 100 |
| • Design Spec | • 生成设计文档 | 30 | 20 |
| • Design Review | • 设计复审 | 10 | 10 |
| • Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 20 |
| • Design | • 具体设计 | 60 | 70 |
| • Coding | • 具体编码 | 600 | 470 |
| • Code Review | • 代码复审 | 30 | 70 |
| • Test | • 测试(自我测试,修改代码,提交修改) | 200 | 420 |
| Reporting | 报告 | 80 | 115 |
| • Test Report | • 测试报告 | 30 | 30 |
| • Size Measurement | • 计算工作量 | 20 | 35 |
| • Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 50 |
| 合计 | 1085 | 1318 |
三. 解题思路
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.指令正确性判断
- 首先先判断是否为空行,若是空行跳过
- 判断是否为total,是的话则输出奖牌总榜
- 判断是否为schedule开头的字符串,若schedule后的日期并不在冬奥期间,则输出N/A,若指令正确,则 输出每日赛程
- 其余指令输出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类
public class OlympicSearch
{
//br用于读input.txt文件,bw用于写output.txt文件
BufferedReader br;
BufferedWriter bw;
//根据传入的路径初始化 br和 bw
public OlympicSearch(String input,String output)
//获取 br
public BufferedReader getBufferReader()
//获取 bw
public BufferedWriter getBufferWriter()
//判断日期是否正确,传入的参数为用空格分割指令得到的字符串数组
public static boolean rightDate(String[] strs)
public static void main(String[] args)
}
Lib类
public class Lib
{
//用于存储对应输出结果的字符串,以便重复指令可直接输出该字符串,提高性能
String medalsString;
Map<String,String> matchesString;
BufferedWriter bw;
//获取OlympicSearch的属性bw
Lib(BufferedWriter bw)
//读取date.json文件,将该日期下输出结果的字符串存储在matchesString中
void initMatches(String date) throws IOException
//读取total.json文件,将输出结果的字符串存储在medalsString中
void initMeadls() throws IOException
//main方法中在指令正确情况下调用该函数
void printMedals() throws IOException
//main方法中在指令正确情况下调用该函数
void printMatches(String date) throws IOException
}
Medals类(用于Gson匹配json数据)
class Medals
{
String rank;
String countryid;
String gold;
String silver;
String bronze;
String count;
//对得到的json数据进行处理
public void changeData()
//转换成字符串
public String toString()
}
Matches类(用于Gson匹配json数据)
class Matches
{
String startdatecn;
String itemcodename;
String title;
String homename, awayname;
String venuename;
//对得到的json数据进行处理
public void changeData()
//转换成字符串
public String toString()
}
五.关键代码展示
当输入指令为 schedule 0202 时,main函数会调用 Lib.printMatches("0202") 方法
- 首先通过 matchesString.containsKey("0202") 判断是否以前存储过0202这一天的输出内容
- 若无就需要调用 Lib.initMatches("0202") 从 0202.json 文件中提取数据并转换为字符串形式存储到 matchesString 中
- 向 output.txt 中写入 matchesString.get("0202")
void printMatches(String date) throws IOException
{
if(!matchesString.containsKey(date))
{
try
{
initMatches(date);
}
catch (IOException e)
{
System.out.println("读取"+date+".json文件失败");
}
}
bw.write(matchesString.get(date));
bw.flush();
}
此处用到Gson并不是直接对整个json数组解析 ,而是通过 StringBuilder 和 BufferedReader 将json文件划分成一个个对象,转换为一个个Matches类,逐个存储到 List<Matches> matchesList中,最后用for循环将matchesList中的每个Matches通过调用 Matches.toString() 方法得到的字符串拼接在一起,赋值给 matchesString
void initMatches(String date) throws IOException
{
Gson gson = new Gson();
String content = "";
List<Matches> matchesList=new ArrayList<Matches>();
StringBuilder builder = new StringBuilder();
String filePath = "src\\data\\schedule\\" + date + ".json";
File file = new File(filePath);
if (!file.exists()) System.out.println("找不到文件"+date+".json");
InputStreamReader streamReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(streamReader);
//对读取到的json数据划分成单个对象的字符串,再用gson转化成match类
while ((content = bufferedReader.readLine()) != null)
{
if (content.equals(" },") || content.equals(" }"))//根据”...“划分
{
builder.append(" }");
Matches match = gson.fromJson(builder.toString(), Matches.class);
match.changeData();
matchesList.add(match);
builder.delete(0, builder.length());//每提取完一个match类,对builder读取到的内容清空
if (content.equals(" }")) break;
} else builder.append(content);
if (content.equals(" \"matchList\": [")) builder.delete(0, builder.length());//将无效内容删除
}
//将所需的输出内容存储到Map<String,String> matchesString中
builder.delete(0, builder.length());
for (int i=0;i<matchesList.size();i++)
{
builder.append(matchesList.get(i).toString());
if(i<matchesList.size()-1) builder.append("\n");
}
matchesString.put(date,builder.toString());
bufferedReader.close();
streamReader.close();
}
当输入指令为total时,main函数调用Lib.printMedals()方法
相关部分代码与输入指令为 schedule 0202相似,就不再细说
main函数流程图

六.性能改进
- 原本的代码 :
将数据存储在 Map<String,List<Matches>> matchesMap 和List<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测试代码运行情况:
改进前:

改进后:

性能分析
改进前:

改进后:

七.单元测试
以下为部分单元测试代码
@Test
public void testrightDate()
{
search=new OlympicSearch("E:\\软件工程\\OlympicSearch\\input.txt","E:\\软件工程\\OlympicSearch\\output.txt");
lib=new Lib(search.getBufferWriter());
try
{
lib.printMedals();
lib.initMatches("0202");
lib.printMatches("");
lib.initMatches("0201");
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Test
public void testrightDate()
{
assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule","0202"}));
assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule","0220"}));
assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule","0213"}));
assertEquals(true,OlympicSearch.rightDate(new String[]{"schedule"," ","0202"}));
assertEquals(false,OlympicSearch.rightDate(new String[]{"schedule","大时代"}));
assertEquals(false,OlympicSearch.rightDate(new String[]{"schedule","asadsa"}));
}
public void testOlympic()
{
OlympicSearch search1=new OlympicSearch("E:\\软件工程\\OlympicSearch\\input.txt","E:\\软件工程\\OlympicSearch\\output.txt");
OlympicSearch search2=new OlympicSearch("E:\\软件工程\\OlympicSearch\\i1.txt","E:\\软件工程\\OlympicSearch\\t.txt");
}

八.异常处理
- 大部分函数都用throws抛出异常,然后在main函数中用 try-catch 处理
void initMeadls() throws IOException
- 少部分的函数会直接在内部直接用 try-catch 处理
File file = new File("src\\data\\total.json");
if (!file.exists()) System.out.println("找不到文件total.json");
public OlympicSearch(String input,String output)
{
try
{
InputStream in=new FileInputStream(input);
OutputStream out=new FileOutputStream(output);
br=new BufferedReader(new InputStreamReader(in,"UTF-8"));
bw=new BufferedWriter(new OutputStreamWriter(out,"UTF-8"));
}
catch(FileNotFoundException e)
{
System.out.println("文件找不到");
}
catch (UnsupportedEncodingException e)
{
System.out.println("转码异常");
}
}
九.心得体会
- 这次作业本质上是对文件的读取和写入,写代码的时间不长,但是处理bug的时间很长,印象最深刻的就是在cmd运行程序,明明在idea都能正常运行,但是在cmd运行就会输出结果乱码和报错,百度了一天倒是对idea和cmd的配置有了更多了解,最后没有在百度上找到答案,但是在同学和助教的帮助下解决了bug
- 还有关于软件的配置问题也要很小心,比起写代码出问题,那些配置出了问题会让人更害怕,其实对于软件的配置还不够了解,在百度的时候很多东西都是懵懵懂懂的
- 作业一定要提早写,要留充分的时间处理bug,快要到ddl的时候处理bug的状态会差很多
- 软件开发的时候,沟通了解需求是非常有必要的。比如在群里,大家向助教询问各种指令对应的输出情况后,才对需求有了更深入的了解。同时,需求是容易变动的,所以在编写代码的过程中,对函数要做好划分,这样才能在需求变动的情况下方便代码的变更
- 这次作业由于对java的知识都忘得差不多了,所以花了很多时间在查询资料上,还是要勤加编写代码
相关推荐
【软件工程】大作业——软件设计说明书,高分大作业
南京理工大学紫金学院,《软件工程》这门课最后的大作业,是每个小组做一份软件设计说明书,这份就是我们当时的高分作业。
软件工程实践第二次作业
项目 内容 这个作业属于哪个课程 福州大学软件工程实践2022年春-F班 这个作业要求在哪里 软件工程实践第二次作业——个人实战 这个作业的目标 爬取数据、实现一个命令行程序 其他参考文献 CSDN 博客园 文章目录1、GitCode项目地址2、PSP表格3、解题思路描述4、接口设计和实现过程4.1 实体类设计4.2 工具类设计4.2.1 文件工具类4.2.2 验证工具类4.3 核心类设计5、关键代码展示6、性能改进6.1 数据读取优化6.2 数据写入优化6.3 性能分析7、...
软件工程个人总结
这个作业属于哪个课程 构建之法-2021秋-福州大学软件工程 这个作业要求在哪里 软件工程实践总结 团队名称 灵机一栋 这个作业的目标 对本学期的软工实践进行个人、团队总结,提出建议 学号 031902140 一、个人总结 1)对比开篇博客对课程目标和期待,“希望通过实践锻炼,增强计算机专业的能力和就业竞争力”,对比目前的所学所练所得,在哪些方面达到了期待和目标,哪些方面还存在不足,为什么? 软工实践课程让我第一次接触软件的开发,第一次亲身体验软件开发的完整流程由理论走向实践...
软件工程课程设计“作业管理系统”的总结和期望
在当今全球疫情的时期,返校、出行等人员流动无法顺利有序进行的情况下,很多学校的教育方式正逐渐由线下授课方式转换为线上授课方式。而此时,一套完善的线上授课及作业系统便成为切实的需求。因此,为了在教育领域应用计算机帮助教学工作更顺利地进行,解决教师与学生教学生活中远程操作的问题,设计实现该网上作业管理系统。
山东大学软件工程实验报告及作业
1.项目是数据库课设做的火车票售票系统 2.写的SSR 3.没有复习资料,只有老师布置的作业题的答案。


