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

222100222林致超 2024-02-28 22:03:23
这个作业属于哪个课程2302软件工程社区
这个作业要求在哪里软件工程第二次作业--文件读取
这个作业的目标完成文件读取小练习
其他参考文献《阿里巴巴Java开发手册》工程师的能力评估和发展单元测试和回归测试

目录

  • Gitcode项目地址
  • PSP表格
  • 解题思路描述
  • JSON文件读取与处理
  • 数据说明
  • 接口设计和实现过程
  • 项目结构
  • 核心功能接口设计
  • 核心功能实现过程
  • 关键代码展示
  • 主函数
  • 获取数据
  • 性能改进
  • 单元测试
  • 异常处理
  • 命令行参数异常
  • 命令错误异常
  • 文件不存在异常
  • 心得体会

Gitcode项目地址

项目地址
因为提交时重新fork了一边,这里展示原项目的提交记录截图

img

PSP表格

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

解题思路描述

JSON文件读取与处理

为了方便读取和处理JSON文件,我们用到了jackson 这一开源框架。Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。

获取的JSON文件中有的是对象类型,有的是数组类型,为了实现通用的json文件读取,我们使用Jackson中objectMapper.readTree方法,先将json文件读取为一个JsonNode对象,再进行后续的处理。

然后通过观察JSON文件的结构,找到我们需要提取的字段

数据说明

athletes:存储了关于跳水项目的运动员信息。

event:存储了项目名称项目id的对照,通过项目名称可以找到对应的项目id。

results:文件夹下存储以项目id命名的各个项目的成绩。包括总分与每一次比赛的小分。

  • 运动员信息

img

  • 比赛信息

先根据input.text中的命令获取项目名称,然后在event.json中匹配相同的项目名称

img

通过项目ID找到results文件夹下对应的项目成绩文件,一层一层地提取我们需要的字段(FullName, Rank, Score等),获取对应的字段后,再按照格式进行输出即可。

接口设计和实现过程

项目结构

.
├─datas            // json数据文件
└─src
   ├─core        // 核心功能目录
   │  └─impl        // 核心功能实现类
   ├─entity        // 实体对象封装
   └─util        // 工具类
       └─FileUtils // 文件读写工具类

核心功能接口设计

核心功能接口主要包括:

  1. 获取所有运动员信息
  2. 获取给定项目名称对应的成绩
  3. 执行命令并返回结果

img

核心功能实现过程

  1. 先从input.txt中获取command列表
  2. 使用executeCommand函数对命名列表进行逐行处理(包括异常命令的处理)
    1. 根据要求,当命令为players时候,调用getAllPlayersInfo方法,得到并且返回所有运动员信息
    2. 使用正则表达式匹配命名,当命令符合获取结果的命令格式时候,调用getEventResult方法,并且传入项目名称event,得到并返回对应项目的比赛结果
    3. 预期之外的命令给出提示,并且返回错误信息
  3. 得到命令处理的结果,按格式输出到output.txt

其中,在得到结果后,将结果信息封装成一个对象,然后通过返回一个对象列表,再进行后续的格式化、输出等操作,相比于直接返回结果字符串,灵活性更强,复用性更高,符合模块化设计原则,提高了代码的可读性和可维护性。

关键代码展示

主函数

public class DWASearch {
    public static void main(String[] args) {
        // 校验、读取命令行参数
        String inputFile;
        String outputFile;
        // 校验命令行参数
        if (args.length != 2) {
            System.out.println("参数错误,需要包含input和output文件名");
        } else {
            inputFile = args[0];
            outputFile = args[1];
            try {
                // 读取命令
                List<String> commandList =  FileUtils.readCommandList(inputFile);
                // 处理命令
                int count = 0;
                for (String command : commandList) {
                    System.out.println("execute: " + command);
                    String result = impl.executeCommand(command);
                    // 第一条命令覆盖原文件内容,后续的命令缀加
                    try (Writer writer = new FileWriter(outputFile,count!=0)) {
                        writer.write(result);
                        count++;
                    }
                }
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
        }
    }
}

获取数据

代码量较大,以获取运动员信息为例

    /**
     * 获取所有运动员信息
     * @return 运动员信息列表
     */
    public static List<Player> getAllPlayersInfo() {
        List<Player> players = new ArrayList<>();

        try {
            JsonNode playersInfo = FileUtils.readObjectFromJson("E:\\课内学习\\软件工程实践\\作业材料\\作业二\\newCode\\project-java\\222100222\\datas\\athletes.json");
            Iterator<JsonNode> iterator = playersInfo.elements();
            while (iterator.hasNext()) {
                // 遍历国家
                JsonNode countryInfo = iterator.next();
                if (countryInfo.isObject()) {
                    // 从对象中读取需要的字段
                    String countryName = countryInfo.get("CountryName").asText();
                    JsonNode participations = countryInfo.get("Participations");
                    // 遍历该国家的运动员
                    Iterator<JsonNode> playerIterator = participations.elements();
                    while (playerIterator.hasNext()) {
                        // 提取字段
                        JsonNode playerInfo = playerIterator.next();
                        String firstName = playerInfo.get("PreferredFirstName").asText();
                        String lastName = playerInfo.get("PreferredLastName").asText();
                        String fullName = lastName + ' ' + firstName;
                        String gender = playerInfo.get("Gender").asText();
                        gender = gender.equals("0") ? "Male" : "Female";
                        // 创建并且添加Player对象到List
                        players.add(new Player(fullName,gender,countryName));
                    }
                }
           }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }

        return players;
    }

性能改进

本题的大致流程就是读json数据并且解析,这也是性能开销较大的地方,于是很自然的可以想到将已经读取、解析过的文件缓存起来,这样再次执行相同命令或者用到相同的数据时,不需要再进行json文件的读取与解析,而是直接从缓存中获取,由此提高性能。

代码大致如下:

   public static List<Player> getAllPlayersInfo() {
        // 读取缓存的运动员信息
        if (!impl.playersInfo.isEmpty()) {
            System.out.println("(read players info from cache)");
            return impl.playersInfo;
        }
         // 正常读取、解析json文件
         // ......
   }

img

单元测试

对核心模块进行单元测试,主要分为三个部分

  1. 获取所有选手信息
  2. 获取给定项目决赛的结果
  3. 命令的处理

img

img

通过较完备的测试用例对这三个核心模块进行测试,从而达到一个较高的覆盖率,保证测试的有效性

展示部分测试代码:

  • 测试获取比赛结果
    @Test
    public void testGetFinalResult1() throws IOException {
        String event = "women 1m springboard";
        List<Result> resultList = impl.getEventResult(event);
        String correctResult = "MULLER Jette 1 [51.60, 52.00, 51.75, 46.80, 46.80]\n" +
                "ROLLINSON Amy 2 [46.00, 42.90, 50.70, 54.00, 46.80]\n" +
                "SKRZEK Kaja 3 [50.40, 46.80, 48.30, 45.60, 48.75]\n" +
                "BENT-ASHMEIL Desharne 4 [46.80, 50.70, 48.30, 43.20, 45.50]\n" +
                "JASMIN Amelie-Laura 5 [50.40, 45.50, 43.70, 46.80, 43.75]\n" +
                "BLAZOWSKA Aleksandra 6 [43.20, 44.85, 45.60, 53.30, 41.80]\n" +
                "WILSON Aimee 7 [50.40, 44.85, 38.40, 44.20, 50.70]\n" +
                "OH Sooyeon 8 [45.60, 36.80, 46.80, 46.80, 46.80]\n" +
                "LEE Yejoo 9 [43.20, 41.40, 43.20, 41.60, 39.60]\n" +
                "JELINKOVA Tereza 10 [46.80, 42.90, 41.40, 26.40, 38.50]\n" +
                "SHARIA Tekle 11 [38.40, 41.60, 30.00, 30.45, 34.10]\n" +
                "SANTIAGO Dominique 12 [42.00, 18.20, 35.70, 34.50, 32.55]\n";
        String resultStr = "";
        for (Result result : resultList) {
            resultStr += result.getFullName() + " " + result.getFinalRank() + " " +result.getFinalScore() + "\n";
        }
        Assert.assertEquals(correctResult,resultStr);
    }
  • 测试项目名称不存在的情况
    @Test
    public void testGetFinalResult4() throws IOException {
        String event = "women 1000m springboard";
        List<Result> resultList = impl.getEventResult(event);
        Assert.assertEquals("[]",resultList.toString());
    }
  • 测试命令处理
    @Test
    public void testExecuteCommand1(){
        String command = "this is a test command";
        String result;
        String expectedStr = "Error\n-----\n";
        try {
            result = impl.executeCommand(command);
            Assert.assertEquals(expectedStr,result);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void testExecuteCommand2(){
        String command = "result test event";
        String result;
        String expectedStr = "N/A\n-----\n";
        try {
            result = impl.executeCommand(command);
            Assert.assertEquals(expectedStr,result);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

异常处理

命令行参数异常

      // 校验命令行参数
        if (args.length != 2) {
            System.out.println("参数错误,需要包含input和output文件名");
        } 

命令错误异常

img

文件不存在异常

捕获并且会在控制台中进行提示

心得体会

在完成这项任务时,我掌握了如何从网络源获取数据,并通过编程手段对这些数据进行深入分析和处理。最具挑战性的部分在于理解如何精准地利用API接口提取数据,以及怎样从结构复杂的JSON数据中筛选和整理出我们需要的关键信息。同时,我也学会了如何有效地从文件中读取数据,对它们进行处理,然后将最终结果存回文件中。这项作业不仅锻炼了我的API调用技巧、JSON数据处理能力,还加深了我对文件操作的理解以及对项目规范的要求。这使我更有信心在未来的项目中面对类似的数据处理挑战。

...全文
124 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

122

社区成员

发帖
与我相关
我的任务
社区描述
FZU-SE
软件工程 高校
社区管理员
  • LinQF39
  • 助教-吴可仪
  • 一杯时间
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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