软件工程第二次作业--文件读取

这个作业属于哪个课程2302软件工程
这个作业要求在哪里软件工程第二次作业–文件读取
这个作业的目标完成对世界游泳锦标赛跳水项目相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序
其他参考文献《构建之法》、《阿里巴巴Java开发手册》《单元测试和回归测试》


gitcode项目地址

selfsuki / Project Java


PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1020
• Estimate• 估计这个任务需要多少时间1020
Development开发8201130
• Analysis• 需求分析 (包括学习新技术)12070
• Design Spec• 生成设计文档30100
• Design Review• 设计复审2050
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)1010
• Design• 具体设计4060
• Coding• 具体编码360480
• Code Review• 代码复审120180
• Test• 测试(自我测试,修改代码,提交修改)120180
Reporting报告120260
• Test Repor• 测试报告60150
• Size Measurement• 计算工作量1030
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划5080
合计9501410

解题思路描述


总体思路

首先,要从网站上获取数据,通常是json数据文件
其次,获取数据后要使得数据变得更容易处理
再次,处理完数据后,要使用json的分析工具,解析json数据
最后,处理解析后的json数据,按题目要求,根据指令,输出结果


获取网站数据

利用开发者工具
从网站的网络请求手动获得我们需要的数据.json文件
根据网站的events.json里对应ID
可以获得各个运动项目的结果的.json文件
也可以获得运动员数据athlete.json
爬数据
获得完数据后将其保存好,进行下一步处理


处理网站数据

为了更好利用程序的输入指令
因此将获取的数据对照event.json用ID对应比赛项目的名称
重新命名,以便更好的使用数据
重新命名了比赛结果文件的名称


利用fastjson处理json数据

利用Maven管理项目,导入fastjson包,对得到的json数据进行处理
关键代码如下(非完整代码):
在maven项目中的pom.xml里导入fastjson包

        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>2.0.9.graal</version>
       </dependency>

处理json数据的例子

        JSONArray heats= parseJsonInputStream(inputStream);
        JSONObject heat = heats.getJSONObject(0);
            JSONArray results = heat.getJSONArray("Results");
                for (Object resultObj : results) {
                    JSONObject result = (JSONObject) resultObj;
                    String fullName=result.getString("FullName");

                    if(fullName.contains(" / ")){
                        outputSynchronizedSortedName(fullName,writer);
                    }
                    else{
                        //写入
                    }

                    //写入
                    StringBuilder scores = new StringBuilder("Score:");
                    JSONArray dives = result.getJSONArray("Dives");
                    double totalPoints = 0;
                    for (Object obj : dives) {
                        JSONObject dive = (JSONObject) obj;
                        scores.append(dive.getString("DivePoints"));
                        totalPoints += Double.parseDouble(dive.getString("DivePoints"));
                        if (dives.indexOf(obj) < dives.size() - 1) {
                            scores.append(" + ");
                        }
                    }
                    //写入
                }

接口设计和实现过程

由于实现的功能较少,我并没有划分具体的软件包
项目通过四个类实现,分别是
DWASearch.class
DivingResult.class
Athlete.class
AthleteDataParser.class

DivingResult类是存储比赛结果的数据结构
Athlete类是存储运动员信息的数据结构
AthleteDataParser类是对运动员信息进行解析的功能类
DWASearch类是程序的驱动类,用于分析输入的文件指令,输出相对应的结果
DWASearch类中还囊括了对比赛结果的解析

关键代码展示

DWASearch类,读取指令,执行指令

    /**
     * 读取文件里的指令
     * @param fileName 文件名
     * @return 读取到的指令作为字符串列表
     */
    private static List<String> readCommandsFromFile(String fileName) {
        List<String> commands = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = reader.readLine()) != null) {
                commands.add(line);
            }
        } catch (IOException e) {
            System.out.println("Error reading input file: " + e.getMessage());
            return null;
        }
        return commands;
    }

    /**
     * 执行指令
     * @param command 读取到的指令
     * @param writer 缓存写入器
     */
    private static void executeCommand(String command, BufferedWriter writer)
            throws IOException {
        if (command.startsWith("result ")) {
            if(command.endsWith(" detail")){
                outputDetailResults(getDetailedResultsMap(command,writer), writer);
            }
            else
                outputFinalResults(command, writer);
        } else if (command.equals("players")) {
            outputPlayersInfo(writer);
        } else {
            writer.write("Error\n-----\n");
        }
    }

DWASearch类,AthleteDataParser类关键代码:
1.处理运动员信息
2.写入,更新详细比赛结果

    /**
     *
     * @param jsonFilePath json文件路径
     * @return 运动员列表
     */
    public static List<Athlete> parseAthleteData(String jsonFilePath) {

        List<Athlete> athletes = new ArrayList<>();

        try (InputStream is = AthleteDataParser.class.getResourceAsStream(jsonFilePath)) {
            if (is == null) {
                return null;
            }

            byte[] data = is.readAllBytes();

            // 读取JSON文件内容为字符串
            String jsonStr = new String(data, StandardCharsets.UTF_8);
            JSONArray countries = JSON.parseArray(jsonStr);

            for (Object countryObj : countries) {
                JSONObject country = (JSONObject) countryObj;
                JSONArray participations = country.getJSONArray("Participations");
                for (Object participationObj : participations) {
                    JSONObject participation = (JSONObject) participationObj;
                    Athlete athlete = new Athlete();
                    athlete.setPreferredLastName(participation.getString("PreferredLastName"));
                    athlete.setPreferredFirstName(participation.getString("PreferredFirstName"));
                    athlete.setNAT(participation.getString("NAT"));
                    athlete.setGender(participation.getIntValue("Gender"));
                    athletes.add(athlete);
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return athletes;
    }

    /**
     * 获得详细结果映射表
     * @param command 读取到的指令
     * @param writer 缓存写入器
     * @return 运动员名字与比赛结果的一对一映射表
     */
    private static Map<String, DivingResult> getDetailedResultsMap(String command, BufferedWriter writer)
            throws IOException {
        DivingResult d = new DivingResult();
        String fileName = command.replace("result ", "")
                .replace(" detail", "") + ".json";
        String filePath="/data/results/" + fileName;

        InputStream inputStream=d.getClass().getResourceAsStream(filePath);

        if(inputStream==null){
            writer.write("N/A\n-----\n");
            return null;
        }

        Map<String, DivingResult> resultMapping = new HashMap<>();
        JSONArray heats= parseJsonInputStream(inputStream);

        for (Object heatObj : heats) {
            JSONObject heat = (JSONObject) heatObj;
            JSONArray results = heat.getJSONArray("Results");

            for (Object resultObj : results) {
                JSONObject resultJson = (JSONObject) resultObj;
                String playerKey = resultJson.getString("FullName");
                DivingResult divingResult = resultMapping.getOrDefault(playerKey, new DivingResult());

                updateDetailResult(divingResult, resultJson, heat);
                resultMapping.put(playerKey, divingResult);
            }
        }
        return resultMapping;
    }


    /**
     * 更新比赛结果映射表
     * @param divingResult 已知的比赛结果
     * @param resultJson 结果的Json对象
     * @param heat 比赛的Json对象
     */
    private static void updateDetailResult(DivingResult divingResult, JSONObject resultJson, JSONObject heat) {
        String heatName = heat.getString("Name");
        switch (heatName) {
            case "Final" -> {
                divingResult.setFinalRank(resultJson.getInteger("Rank"));
                divingResult.setFinalScores(getResultScores(resultJson));
            }
            case "Semifinal" -> {
                divingResult.setSemiRank(resultJson.getInteger("Rank"));
                divingResult.setSemiScores(getResultScores(resultJson));
            }
            default -> {
                divingResult.setPrelimRank(resultJson.getInteger("Rank"));
                divingResult.setPrelimScores(getResultScores(resultJson));
            }
        }
        divingResult.setFullName(resultJson.getString("FullName"));
    }

思路解释:通过解析json文件,获取json数组,通过两个模型类来存储信息,用FileWriter来输出到对应的输出文件
独到之处:
1.利用HashMap来存储详细比赛结果,实现比赛结果与选手的一对一映射表,实现快速存取排序。
2.代码复用率高,简洁明了。
3.利用处理过后的文件名,可以根据指令简单获取到相应的数据。


性能改进


使用BufferReader & BufferWriter缓冲存储,简化代码逻辑

1.代码中的Writer与Reader均使用缓存存取
在读取和写入文件时使用缓冲流,减少IO操作,提高读写效率。
2.简化了switch,以及几个函数的代码逻辑
效果如下:

输入players指令优化前后:
优化前:94msplayers优化前
优化后:94ms->76ms
优化后


输入多行Final Result指令:
优化前:165ms多行优化前
优化后:165ms->147ms
优化后


输入多行Detail Result
优化前:198ms请添加图片描述
优化后:198ms->169ms
请添加图片描述

可见性能的提升还是比较可观的

单元测试

设计样例代码

单元测试设计样例如下:

    @Test
    public void testNoParams() {
        //错误样例-没有参数
        DWASearch.main(new String[]{});

        // Verify the results
    }
    @Test
    public void testOneParams() {
        //错误样例-参数太少
        DWASearch.main(new String[]{"input.txt"});

        // Verify the results
    }
    @Test
    public void testManyParams() {
        //错误样例-参数太多
        DWASearch.main(new String[]{"input.txt","output.txt","wow.txt"});

        // Verify the results
    }
    @Test
    public void testWrongParams() {
        // 错误样例-参数输入错误
        DWASearch.main(new String[]{"input"});

        // Verify the results
    }
    @Test
    public void testFileNotFound() {
        // 错误样例-找不到文件
        DWASearch.main(new String[]{"args.txt","output.txt"});

        // Verify the results
    }
    @Test
    public void testCorrectWithNoCommand() {
        // 正确样例-无指令
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
            // 写入空字符串来清空文件内容
              writer.write("");
        } catch (IOException e) {
            System.out.println("Error writing to input file: " + e.getMessage());
        }
            DWASearch.main(new String[]{"input.txt","output.txt"});

        // Verify the results
    }
    @Test
    public void testCorrectWithFinalResult() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
            // 写入空字符串来清空文件内容
            writer.write("");
            writer.write("result women 1m springboard");
        } catch (IOException e) {
            System.out.println("Error writing to input file: " + e.getMessage());
        }
        // 正确样例-指令:result women 1m springboard
        DWASearch.main(new String[]{"input.txt","output.txt"});

        // Verify the results
    }
    @Test
    public void testCorrectWithDetailResult() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
            // 写入空字符串来清空文件内容
            writer.write("");
            writer.write("result women 3m synchronised detail");
        } catch (IOException e) {
            System.out.println("Error writing to input file: " + e.getMessage());
        }
        // 正确样例-指令:result women 3m synchronised detail
        DWASearch.main(new String[]{"input.txt","output.txt"});

        // Verify the results
    }
    @Test
    public void testCorrectWithMultiLineCommand() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
            // 写入空字符串来清空文件内容
            writer.write("");
            writer.write(
                    """
                            player
                            Players
                            resultwomen 1m springboard
                            result women 10m springboard
                            result sss
                            result detail
                            result women 1m springboard details
                            result men 10m     synchronised
                            players""");
        } catch (IOException e) {
            System.out.println("Error writing to input file: " + e.getMessage());
        }
        // 正确命令行输入样例-复杂多行指令
        DWASearch.main(new String[]{"input.txt","output.txt"});

        // Verify the results
    }
    @Test
    public void testCorrectWithPlayers() {
        //
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
            // 写入空字符串来清空文件内容
            writer.write("");
            writer.write("players");
        } catch (IOException e) {
            System.out.println("Error writing to input file: " + e.getMessage());
        }
        // 正确样例-指令:players
        DWASearch.main(new String[]{"input.txt","output.txt"});

        // Verify the results
    }
    @Test
    public void testCorrectWithMultiLineFinalCommand() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
            // 写入空字符串来清空文件内容
            writer.write("");
            writer.write(
                    """
                            result women 1m springboard
                            result women 3m springboard
                            result women 10m platform
                            result women 3m synchronised
                            result women 10m synchronised
                            result men 1m springboard
                            result men 3m springboard
                            result men 10m platform
                            result men 3m synchronised
                            result men 10m synchronised""");
        } catch (IOException e) {
            System.out.println("Error writing to input file: " + e.getMessage());
        }
        // 正确命令行输入样例-复杂多行指令
        DWASearch.main(new String[]{"input.txt","output.txt"});

        // Verify the results
    }
    @Test
    public void testCorrectWithMultiLineDetailCommand() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
            // 写入空字符串来清空文件内容
            writer.write("");
            writer.write(
                    """
                            result women 1m springboard detail
                            result women 3m springboard detail
                            result women 10m platform detail
                            result women 3m synchronised detail
                            result women 10m synchronised detail
                            result men 1m springboard detail
                            result men 3m springboard detail
                            result men 10m platform detail
                            result men 3m synchronised detail
                            result men 10m synchronised detail""");
        } catch (IOException e) {
            System.out.println("Error writing to input file: " + e.getMessage());
        }
        // 正确命令行输入样例-复杂多行指令
        DWASearch.main(new String[]{"input.txt","output.txt"});

        // Verify the results
    }

自动化测试与覆盖率

通过使用SquareTest的idea插件可以实现自动化测试,设计测试样例
覆盖率较高
对错误输入也有存在异常处理,输出错误的文本


异常处理

我的程序对基本上所有可能存在错误输入的参数以及所有可能存在的空指针进行异常处理
通常为输出错误提示信息或文本
例子如下:

            if (athletes != null) {
                for (Athlete athlete : athletes) {
                    writeAthleteInfo(writer, athlete);
                }
                System.out.println("Successfully wrote athlete information to output file.");
            }
        else{
            writer.write("N/A\n-----\n");
        }
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = reader.readLine()) != null) {
                commands.add(line);
            }
        } catch (IOException e) {
            System.out.println("Error reading input file: " + e.getMessage());
            return null;
        }

心得体会

通过本次作业
我学习到了以下内容:
1.使用Maven管理项目,通过maven来增加项目依赖
2.使用SquareTest插件来进行项目的单元测试
3.使用fastjson工具来解析并处理json文件
4.懂得使用缓存写入读取
5.大致了解了软件项目提交git管理的流程与做法,体会了项目管理
6.本次项目给我了莫大的信心克服了来自ddl的恐惧 ,逐步推进完成项目,自信心增强不少。

请多多批评指教,感谢!

Sat Mar 02 2024 17:45:35 GMT+0800.


  • 28
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

selfsuki

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值