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

221900214何震东 学生 2022-03-04 11:34:50
这个作业属于哪个课程2022年福大-软件工程、实践-W班
这个作业要求在哪里软件工程实践第二次作业——个人实战
这个作业的目标完成对冬奥会的赛事数据的收集,并实现一个能够对国家排名及奖牌个数统计的控制台程序。
其他参考文献Fastjson 简明教程

目录

目录

  • Gitcode项目地址
  • PSP表格
  • 解题思路描述
  • 获取数据
  • 解析json
  • 读取input.txt的指令
  • 接口的设计与实现过程
  • 涉及类
  • 主要函数
  • 函数调用关系
  • 关键函数流程图
  • 关键代码展示
  • 入口主函数代码
  • 获取input.txt指令并转换成String列表
  • 验证指令正确性并返回结果
  • 性能改进
  • 改进前性能分析
  • 改进思路
  • 改进后性能分析
  • 单元测试
  • 异常处理
  • UnsupportedEncodingException
  • IOException
  • FileNotFoundException
  • 心得体会


Gitcode项目地址

项目地址

PSP表格

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

解题思路描述

【该爬取行为仅用于课程教学】

获取数据

在东奥专栏打开每日赛程,在浏览器用F12打开开发者工具,选择网络按钮,点击对应的日期

img

分析请求标头可以得到发送GET请求的链接
爬取数据我使用python里的requests包的get(url)方法读出response,
将用response.text()读出返回文本数据r
用r[3:-2]掐头去尾,并且将r字符串中的"\/"替换为"/"
然后利用r.encode("utf8").decode('unicode_escape')语句将r从Unicode编码转为utf8
最后写入data文件夹中
代码如图

img

解析json

编写两个实体类Medal和Match用来分别接收奖牌和比赛数据

img

img

利用第三方jar包fastjson进行解析得到Medal列表如下,Match也是类似

img

读取input.txt的指令

利用输入流将input的指令全部读入
利用String类的split("\\\n")方法将读入的字符串变成String列表
将指令首尾空格去掉后判断首字符是否为t或s,否的话输出Error
否则进一步判断是否是total,如是输出总榜单,不是则判断前面八个字符是否是schedule,否则输出Error,是的话判断后面是否带有空格加指定范围内的日期,是则输出对应日期的比赛列表,否则输出N/A


接口的设计与实现过程

涉及类

public class OlympicSearch {}
//调用Lib类的函数从input.txt中读取指令,验证指令,根据指令输出选择对应输出
public class Lib {}
//Lib类,提供所有的工具函数,包括读取文件函数,读取指令函数,写入文件函数,json解析函数,输出格式控制函数,验证指令函数
public class Match {}
//用来存放从json数据读取的赛事信息,位于pojo软件包下
public class Medal {}
//用来存放从json数据读取的奖牌榜信息,位于pojo软件包下

主要函数

OlympicSearch类

public static void main(String[] args) {}
//程序入口,调用函数,读入指令,并且进行判断及输出

Lib类

public String readFile(String fileName){}
//读取data下的json文件
public String readInputTxt(String fileName){
//读取input.txt文件
public void outputFile(String fileName,String content){}
//将指定内容输出对应文件
public void deleteFile(String fileName){}
//删除指定文件
public List<String> getInstructionList(String fileName){}
//从input.txt文件读取指令并将其转换为List<String>类型
public List<Medal> readTotal(String totalFIle){}
//读取total.json文件并将其解析为List<Medal>
public List<Match> readMatch(String matchFile){}
//读取schedule XXXX.json文件并将其解析为List<Match>
public String getOutputTotal(List<Medal> medalList){}
//List<Medal>类型的列表转换成题目要求输出格式
public String getOutPutMatch(List<Match> matchList){}
//List<Match>类型的列表转换成题目要求输出格式
public String validateInstruction(String instruction){}
//验证指令

函数调用关系

img

关键函数流程图

img

关键代码展示

入口主函数代码

代码开始,如果给定的输出文件已存在,先删除,
然后遍历指令,验证指令,按照对应指令得到对应输出值,拼接到存放文件输出字符串outputContent中,
拼接一定数量后outputContent输出到指定输出文件,
对于合法指令,先判断是否在hashMap中存在,存在则直接从hashMap中取
不存在则将其放到hashmap中

Lib lib = new Lib();
if (!lib.deleteFile(outputFile)) {
    return;
}
Map<String,String> map = new HashMap<>();
StringBuilder outputContent = new StringBuilder("");
List<String> instructionList =  lib.getInstructionList(inputFile);
int cnt = 0;
for (String instruction : instructionList) {
    cnt++;
    String validateResult = lib.validateInstruction(instruction);
    if (validateResult.equals("total")) {
        String outputTotal = "";
        if (map.containsKey("total")) {
            outputTotal = map.get("total");
        }
        else {
            List<Medal> medalList = lib.readTotal(totalFIle);
            outputTotal = lib.getOutputTotal(medalList);
            map.put("total",outputTotal);
        }
        outputContent.append(outputTotal);
    }
    else if (validateResult.equals("Error")||validateResult.equals("N/A")) {
        outputContent .append(validateResult).append("\n-----\n");
    }
    else {
        String date = validateResult;
        String outputMatch = null;
        if (map.containsKey(date)) {
            outputMatch = map.get(date);
        }
        else {
            outputMatch = lib.getOutPutMatch(lib.readMatch(matchFilePrefix+date+".json"));
            map.put(date,outputMatch);

        }
            outputContent.append(outputMatch);
    }
    if (cnt > 10000 ) {
        lib.outputFile(outputFile,outputContent.toString());
        outputContent = new StringBuilder("");
        cnt = 0;
    }

}
lib.outputFile(outputFile,outputContent.toString());

获取input.txt指令并转换成String列表

将输入指令全部读入,然后利用split("\\\n")分割出每一行,
然后将分割出的每行用strip()将收尾空格去除,如果结果为空则说明此行为空行,跳过
否则放到String列表中,最后将String列表返回。

public List<String> getInstructionList(String fileName) {
    List<String> instructionList = new ArrayList<>();
    String content = readInputTxt(fileName);
    for (String s : content.split("\\\n")) {
        s=s.strip();
        if (!s.equals(""))
            instructionList.add(s);
    }
    return instructionList;
}

验证指令正确性并返回结果

参数传入已去除首尾空格的指令列表
初始化验证结果为Error,判断指令首字母是否为t或者s,
如果为t,判断其是否是total,是则返回total,否则返回Error
如果为s判断指令长度是否小于12或者前八个字符是否为schedule,
如果是则返回Error,否则进一步判断schedule后面是有空格
无则返回Error,有则进一步判断其是否符合指定的日期格式
否则返回N/A,是则返回对应的日期
最后特判一下指令是schedule这种情况,是则返回N/A

public String validateInstruction(String instruction) {
    instruction = instruction.strip();
    String validateAns = "Error";
    if (instruction.substring(0,1).equals("t")) {
        if (instruction.length()!=5||(!instruction.equals("total")))
            validateAns = "Error";
        else
            validateAns = "total";
    }
    else if(instruction.substring(0,1).equals("s")) {
        if (instruction.length()<12||(!instruction.substring(0,8).equals("schedule")))
            validateAns = "Error";
        else {
            String date = instruction.substring(8,instruction.length());
            if (!date.substring(0,1).equals(" "))
                validateAns = "Error";
            else {
                date = date.strip();
                if (date.length()!=4 || (!date.matches("\\d\\d\\d\\d")))
                    validateAns = "N/A";
                else if (!((date.matches("020[2-9]"))||date.matches("021[0-9]")||date.matches("0220")))
                    validateAns = "N/A" ;
                else
                    validateAns = date;
            }
        }
        if (instruction.equals("schedule"))
            validateAns = "N/A";
    }
    return validateAns;
}

性能改进

改进前性能分析

用一百万行有效指令测试,跑了四十几分钟都停不下来,内存最高消耗4、5G,写了个3A大作,电脑原地起飞

img

改进思路

经过对各部分代码的时间测速,我发现每次从json文件读入进行解析以及String的拼接耗费时间大,于是我用hashmap将已访问过的数据储存到内存,若重复访问时则直接到hashmap取出,
并且对于次数很高的字符串拼接不采用String+String的形式,而采用StringBuilder.append()。
并且将输出数据拼接到一定大小才输出,而不是一读指令就输出。

改进后性能分析

时间变成11秒,内存消耗变成两三百M

img

img

单元测试

主要对验证函数进行测试

img

覆盖率测试

img

异常处理

UnsupportedEncodingException

输入流不支持相应编码时会出错,这个异常在读入文件并指定编码时可能会出现

img

IOException

文件读写可能会出现异常

img

FileNotFoundException

文件找不到异常,指定读写的文件时可能会出现该异常

img


心得体会

不知道怎么开始做作业的时候,可以先写个大概的博客,确定大致的步骤
遇到问题不过分盲目调试,应学会百度
应学会如何定位问题,学会测试代码各个部分的大致运行时间进行优化
应一开始思考大致的写法,应避免在后期再大改特改
不要过分相信自己的时间预估能力,应多安排点时间灵活机动写作业

...全文
559 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
代码下载地址: https://pan.quark.cn/s/8236006bf1f9 Word精灵插件:一款用于增强Microsoft Word功能的辅助软件,能够将多种复杂功能转化为插件形式,并在软件状态栏中进行展示,涵盖诸如批注管理、表格处理、内容替换、文档拆分、数学运算、字符提取、批量重命名等多项实用工具。在工作环境中应用该插件能够显著降低工作强度,提升操作效率。Word精灵插件兼容32位与64位的Microsoft Word版本,支持Word 2007、2010、2013以及Word 2016操作系统,但不适用于Word 2003版本。此外,该插件同样支持WPS办公软件。 功能概述: 1、表格自动调整宽度:自动优化文档内所有表格的显示宽度。 2、批量导出批注信息:将文档内所有批注集中导出到Excel工作簿中。 3、表格至Excel多表导出:在将表格导出到Excel时,每个Word表格将独立存放在一个工作表中,Word文档内的表格数量与Excel生成的工作表数量相等,并附有工作表目录。 4、表格至Excel单表导出:将文档内所有表格整合后导出到一个Excel工作表中,多个表格将按顺序排列于同一工作表内。 5、统一图片分辨率:对指定文件夹内的所有图片进行分辨率标准化处理。 6、图片批量缩放:依据设定比例对图片进行放大或缩小,支持按百分比调整。 7、图片批量插入:将图片批量插入到当前文档,可选择图片名称的展示形式,并设定图片的高度。 8、图片格式统一转换:将指定文件夹内的所有图片转换为相同的文件格式。 9、内容批量替换:对文档内容、页眉及页脚执行批量替换操作,例如将数字1替换为字母A,数字2替换为字母B,数字3替换为字母C等。 10、图片批量导出:将文档内所...
打开链接下载源码: https://pan.quark.cn/s/245ca7a27256 OmniGraffle是一款效能卓越的图形设计软件,在构建图表、流程图以及组织结构图等领域的应用尤为突出。该软件起源于Mac操作系统,并且兼容iOS平台,作为专业人士及业余爱好者进行图形设计时的首选工具之一。在OmniGraffle的功能模块中,“泳道图流程图”占据着核心地位,它主要用于勾勒业务流程图或系统流程图,其中各个分隔的泳道象征着不同的职能角色、部门划分或工作流程的各个阶段。泳道图(Lanes Diagram)作为流程图的一种特殊形式,通过将流程中的各个操作步骤分配到垂直或水平的“泳道”之中,能够明确地揭示出每个参与方或部门所承担的责任以及整个流程的走向。此类图形通常应用于业务流程管理(BPM)和系统分析领域,旨在帮助用户深入理解并优化复杂的业务流程。 在OmniGraffle中构建泳道图时,由于软件本身并未提供现成的泳道图模板,用户需要自行设计图形和布局以模拟出泳道的效果。然而,您提供的"06stencil泳道图流程图.graffle"文件很可能是一个预先构建好的模板,能够显著简化这一过程。该模板可能包含了预先设计好的泳道形态、箭头以及其他流程图组件,使用户能够直接在此基础上进行修改和增添个人的步骤,从而节省了大量的设计时间。 应用OmniGraffle的泳道图模板,你可以: 1. **导入模板**:首先需要启动OmniGraffle并将"06stencil泳道图流程图.graffle"文件添加到你的项目工作中。 2. **定制泳道**:依据实际需求调整泳道的数量和尺寸,使之契合你的业务流程。每个泳道对应一个角色或部门,确保它们的排列顺序和宽度能够精确地体现实际的工...

142

社区成员

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

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