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

Extazy 2024-02-27 16:34:01
这个作业属于哪个课程福州大学-202302软件工程实践
这个作业要求在哪里软件工程第二次作业--文件读取
这个作业的目标实现一个对世界游泳锦标赛跳水项目相关赛事数据进行统计的控制台程序
其他参考文献构建之法、码出高效_阿里巴巴Java开发手册

目录

  • 一、Gitcode项目地址
  • 二、PSP表格
  • 三、解题思路描述
  • 3.1、获取数据
  • 3.2、解析数据
  • 3.3 问题一的实现
  • 3.4 问题二的实现
  • 四、接口设计和实现过程
  • 4.1 主要的类
  • 4.2 函数设计
  • 4.3 指令处理流程图
  • 4.4 算法的关键和独到之处
  • 五、关键代码展示
  • 5.1 指令处理
  • 5.2 数据模型类
  • 5.3 缓存函数
  • 六、性能改进
  • 七、单元测试
  • 八、异常处理
  • 8.1 命令行参数校验
  • 8.2 I/O异常处理
  • 九、心得体会

一、Gitcode项目地址

仓库地址

二、PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2535
• Estimate• 估计这个任务需要多少时间2535
Development开发13001450
• Analysis• 需求分析 (包括学习新技术)350400
• Design Spec• 生成设计文档4030
• Design Review• 设计复审5040
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)2030
• Design• 具体设计4050
• Coding• 具体编码460500
• Code Review• 代码复审4060
• MyTest• 测试(自我测试,修改代码,提交修改)300340
Reporting报告110100
• MyTest Report• 测试报告5550
• Size Measurement• 计算工作量3025
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划2525
合计14351585

三、解题思路描述

3.1、获取数据

  • 打开赛事官网,通过浏览器控制台获取赛事信息

在这里插入图片描述

  • 获取运动员信息步骤同获取赛事信息

在这里插入图片描述

  • 获取具体的结果

    在这里插入图片描述

3.2、解析数据

  • (1) 导入Gson包
  • (2)调用Gson类的默认构造函数来创建一个Gson实例。这个实例可以用来执行序列化和反序列化操作
  • (3)定义一个Java类来映射JSON数据:根据JSON数据的结构,定义一个或多个Java类。确保类的字段名与JSON对象中的键名相匹配。如果键名与Java类的字段名不完全相同,可以使用@SerializedName注解来指定JSON键名,本例中的Java类如下。

在这里插入图片描述

  • (4)解析JSON字符串:使用Gson实例的fromJson方法来解析JSON字符串,并将其转换为Java对象。
  • (5)将Java对象转换回JSON格式的字符串:使用Gson实例的toJson方法。

3.3 问题一的实现

(1)读取JSON文件

  • 定义文件路径filePath,指向存储运动员信息的JSON文件。
  • 读取文件内容,然后将字节内容转换成字符串(new String(..., StandardCharsets.UTF_8))。

(2)解析JSON数据

  • 创建一个Gson对象实例,用于解析JSON字符串。
  • 调用gson.fromJson(content, Country[].class),将JSON字符串转换成Country对象数组。

(3)提取运动员信息

  • 遍历每个国家的参与者列表。
  • 对每个国家的运动员按PreferredFirstName进行字典序排序。
  • 遍历排序后的运动员列表,对每位运动员使用parseAthlete方法生成格式化字符串。

(4)返回格式化字符串

  • 将所有运动员的信息组合成一个大字符串,最后返回这个字符串。

3.4 问题二的实现

(1) 获取赛事ID

  • 调用getEventIdByDisciplineName(name)方法,通过赛事名称获取赛事ID。

(2)读取赛事结果文件

  • 根据赛事ID构造结果文件的路径filePath
  • 读取文件内容,然后将字节内容转换成字符串(new String(..., StandardCharsets.UTF_8))。

(3) 解析赛事结果JSON数据

  • 创建一个Gson对象实例,用于解析JSON字符串。
  • 调用gson.fromJson(content, CompetitionResult.class),将JSON字符串转换成CompetitionResult对象。

(4) 提取决赛结果

  • 遍历CompetitionResult对象的Heats数组,寻找名称为"Final"的项。
  • 对于找到的决赛项,遍历其结果列表Results,对每个结果使用parseFinalResult方法生成格式化字符串。

(5)返回格式化的结果字符串

  • 将所有决赛运动员的结果组合成一个大字符串,最后返回这个字符串。

四、接口设计和实现过程

4.1 主要的类

  • 数据模型类:根据JSON数据的结构设计数据模型类。例如,Athlete、Event、CompetitionResult等,这些类映射了JSON数据的结构,便于使用Gson进行解析。
  • 逻辑处理类:例如,Lib类包含了主要的逻辑处理方法。但在更复杂的应用中,可能需要将逻辑进一步拆分到不同的类中,比如ResultParser用于解析比赛结果,AthleteManager用于管理运动员信息等。

4.2 函数设计

  • 数据解析函数:如parseAthlete和parseFinalResult,它们负责解析单个实体的数据。
  • 批量处理函数:如getAllResults和getAthletes,负责处理和聚合多个实体的数据。
  • 辅助函数:如generateScoreString和formatSynchronisedFullName,提供特定的功能以支持主要逻辑。
  • IO操作函数:如getEventIdByDisciplineName,处理文件读取和JSON到对象的映射。

4.3 指令处理流程图

在这里插入图片描述

4.4 算法的关键和独到之处

  • 数据结构的选择:利用嵌套的Map结构灵活表示复杂的数据关系,既方便存储也便于查询。
  • 模块化设计:通过将逻辑拆分成独立的函数或类,提高了代码的可读性和可维护性。
  • 错误处理:在读取文件、解析JSON时加入异常处理机制,提高程序的健壮性。
  • 灵活的数据处理:代码设计允许轻松扩展新的数据处理逻辑,例如添加新的比赛项目或运动员信息的处理,只需很少或不需要修改现有代码结构。

五、关键代码展示

5.1 指令处理

public static List<Instruction> getInstructionsFromFile(String filePath) throws IOException {
    List<Instruction> instructions = new ArrayList<>();
    List<String> validEvents = Arrays.asList(
            "women 1m springboard", "women 3m springboard", "women 10m platform",
            "women 3m synchronised", "women 10m synchronised",
            "men 1m springboard", "men 3m springboard", "men 10m platform",
            "men 3m synchronised", "men 10m synchronised"
    );

    String content = new String(Files.readAllBytes(Paths.get(filePath)), StandardCharsets.UTF_8);
    String[] lines = content.split("\n");
    for (String line : lines) {
        line = line.trim();
        if (line.startsWith("result ")) {
            String[] parts = line.substring("result ".length()).trim().split(" ", 4); // 最多分割成4部分

            if (parts.length >= 3 && validEvents.contains(String.join(" ", Arrays.copyOf(parts, parts.length - (parts.length == 4 ? 1 : 0))))) {
                String eventName = String.join(" ", Arrays.copyOf(parts, parts.length - (parts.length == 4 ? 1 : 0)));
                boolean isDetail = parts.length == 4 && "detail".equals(parts[3]);
                if (isDetail) {
                    instructions.add(new Instruction("result", eventName + " detail"));
                } else if (parts.length == 3) {
                    instructions.add(new Instruction("result", eventName));
                } else {
                    instructions.add(new Instruction("n/a", null)); // 不符合预期的指令格式
                }
            } else {
                instructions.add(new Instruction("n/a", null)); // 比赛项目名称无效
            }
        } else if ("players".equals(line)) {
            instructions.add(new Instruction("players", null)); // players指令
        } else {
            instructions.add(new Instruction("error", null)); // 无法识别的指令
        }
    }
    return instructions;
}

5.2 数据模型类

public class Event {
    private List<Sport> Sports;

    public static class Sport {
        private List<Discipline> DisciplineList;

    public static class Discipline {
        private String Id;
        private String DisciplineName;
}

public class CompetitionResult {
    List<Heat> Heats;

    public static class Heat {
        String Name;
        List<Result> Results;
    }

    public static class Result {
        private String FullName;
        private int Rank;
        private String TotalPoints;
        private List<Dive> Dives;

    }

    public static class Dive {
        private String DivePoints;
}

public class Country {
    private String CountryName;
    private List<Athlete> Participations;
}

class Athlete {
    private int Gender; // 0 for male, 1 for female
    private String PreferredLastName;
    private String PreferredFirstName;
}

5.3 缓存函数

private static Object getCachedJson(String filePath, Class<?> clazz) throws IOException {
    Object cachedResult = jsonParseCache.get(filePath);
    if (cachedResult == null) {
        String content = fileContentCache.computeIfAbsent(filePath, k -> {
            try {
                return new String(Files.readAllBytes(Paths.get(k)), StandardCharsets.UTF_8);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        });
        cachedResult = gson.fromJson(content, clazz);
        jsonParseCache.put(filePath, cachedResult);
    }
    return cachedResult;
}

六、性能改进

该代码主要用于处理锦标赛相关数据,包括从JSON文件中读取数据、解析运动员信息、赛事结果等。优化的关键点在于减少对同一JSON文件的重复解析。以下是优化策略:

  • 单例模式缓存:由于Gson实例的创建相对昂贵,我们可以将其实例化一次并重用,同时缓存已解析的JSON数据,避免对相同文件的重复解析。

  • 缓存JSON解析结果:对于从文件中读取的JSON数据,特别是在getEventIdByDisciplineName和getFinalResult/getAllResults方法中可能会多次读取同一个文件,可以将首次解析的结果存储在内存中,之后需要时直接从缓存获取。

  • 优化Gson的使用:在整个类中,只需创建一次Gson实例并重复使用它,而不是每次调用方法时都创建新的实例。

    private static Object getCachedJson(String filePath, Class<?> clazz) throws IOException {
        Object cachedResult = jsonParseCache.get(filePath);
        if (cachedResult == null) {
            String content = fileContentCache.computeIfAbsent(filePath, k -> {
                try {
                    return new String(Files.readAllBytes(Paths.get(k)), StandardCharsets.UTF_8);
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                }
            });
            cachedResult = gson.fromJson(content, clazz);
            jsonParseCache.put(filePath, cachedResult);
        }
        return cachedResult;
    }

七、单元测试

对核心的Lib类进行测试,部分测试函数和结果如下

@Test
public void testGetFinalResult() throws IOException {
    Lib lib = new Lib();
    lib.getFinalResult("Women 10m Platform");
    lib.getFinalResult("Men 100m Butterfly");
    lib.getFinalResult("");
    lib.getFinalResult(null);
    lib.getFinalResult("   ");
    lib.getFinalResult("Women 10m Platform Women 10m Platform Women 10m Platform");
    lib.getFinalResult("@#$%^&*");
    lib.getFinalResult("123456");
    lib.getFinalResult("Women10mPlatform2024");
    lib.getFinalResult("Women 10m Platform is a very long event name that might exceed the expected limit");
}

在这里插入图片描述


在这里插入图片描述

八、异常处理

8.1 命令行参数校验

if (args.length != 2) {
    System.out.println("命令行应输入两个参数: <输入文件> <输出文件>");
    System.exit(-1);
}

8.2 I/O异常处理

    try {
        Lib.writeDataToFile(inputFile, outputFile);
    } catch (IOException e) {
        System.out.println("发生错误:" + e.getMessage());
        e.printStackTrace();
    }

九、心得体会

通过这次作业,我深刻体会到了合理安排时间、细读作业要求、性能优化意识。我意识到,仅仅让程序运行并不够,性能优化同样重要,尤其是面对大量数据时。利用互联网,我解决了许多难题,这不仅促进了思路的开拓,也加深了对问题的理解。此外,我学会了JSON数据处理和文件操作。这些技能的学习和应用,不仅提升了我的编程能力,也让我对软件工程的实际应用有了更深入的了解。这次经历不仅是对技术能力的提升,更是对学习方法和解决问题策略的一次成长。

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

122

社区成员

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

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