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

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类

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") 方法

  1. 首先通过 matchesString.containsKey("0202") 判断是否以前存储过0202这一天的输出内容
  2. 若无就需要调用 Lib.initMatches("0202") 从 0202.json 文件中提取数据并转换为字符串形式存储到 matchesString 中
  3. 向 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>> 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测试代码运行情况:

改进前:

在这里插入图片描述

改进后:

在这里插入图片描述

性能分析

改进前:

在这里插入图片描述

改进后:

在这里插入图片描述

七.单元测试

以下为部分单元测试代码

    @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的知识都忘得差不多了,所以花了很多时间在查询资料上,还是要勤加编写代码
...全文
567 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jingbin-Wang 教师 2022-03-05
  • 打赏
  • 举报
回复
3000条指令的测试效果上看改进的效果不是很明显,如果再上两个数量级看看改进的效果会不会明显一点?
221900136_黄依灵 学生 2022-03-06
  • 举报
回复
@Jingbin-Wang 用11万条指令测试:改进前 程序运行时间: 125358ms 改进后 程序运行时间: 8054ms
代码下载地址: 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创作助手写篇文章吧