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

hyl12 hyl12 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区
这个作业属于哪个课程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>> 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的知识都忘得差不多了,所以花了很多时间在查询资料上,还是要勤加编写代码
...全文

相关推荐

【软件工程】大作业——软件设计说明书,高分大作业

南京理工大学紫金学院,《软件工程》这门课最后的大作业,是每个小组做一份软件设计说明书,这份就是我们当时的高分作业。

振华OPPO

软件工程实践第二次作业

项目 内容 这个作业属于哪个课程 福州大学软件工程实践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、...

Kylenax

软件工程个人总结

这个作业属于哪个课程 构建之法-2021秋-福州大学软件工程 这个作业要求在哪里 软件工程实践总结 团队名称 灵机一栋 这个作业的目标 对本学期的软工实践进行个人、团队总结,提出建议 学号 031902140 一、个人总结 1)对比开篇博客对课程目标和期待,“希望通过实践锻炼,增强计算机专业的能力和就业竞争力”,对比目前的所学所练所得,在哪些方面达到了期待和目标,哪些方面还存在不足,为什么? 软工实践课程让我第一次接触软件的开发,第一次亲身体验软件开发的完整流程由理论走向实践...

请给我个好听的备注

软件工程课程设计“作业管理系统”的总结和期望

在当今全球疫情的时期,返校、出行等人员流动无法顺利有序进行的情况下,很多学校的教育方式正逐渐由线下授课方式转换为线上授课方式。而此时,一套完善的线上授课及作业系统便成为切实的需求。因此,为了在教育领域应用计算机帮助教学工作更顺利地进行,解决教师与学生教学生活中远程操作的问题,设计实现该网上作业管理系统。

李狄坤

山东大学软件工程实验报告及作业

1.项目是数据库课设做的火车票售票系统 2.写的SSR 3.没有复习资料,只有老师布置的作业题的答案。

夜行少女
评论 2
收藏 已收藏
分享
返回顶部