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

222100302杨婷婷 2024-03-01 21:40:29
这个作业属于哪个课程2302软件工程
这个作业要求在哪里软件工程第二次作业--文件读取
这个作业的目标完成对世界游泳锦标赛跳水项目相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序。
其他参考文献《阿里巴巴Java开发手册》、《构建之法》

目录

  • 1.Gitcode项目地址
  • 2.PSP表格
  • 3.解题思路描述
  • 3.1 选择解析类库
  • 3.2 解析json文件
  • 3.2.1 解析运动员信息数据(AthletesData)
  • 3.2.2 解析比赛信息数据(ResultData)
  • 3.3 从命令行参数中获取的输入和输出文件路径(DWASearch)
  • 4.接口设计和实现过程
  • 5.关键代码展示
  • 6.性能改进
  • 7.单元测试
  • 7.1 main()方法测试
  • 7.2 测试resultDataPrint方法写入数据到指定的文件
  • 7.3 测试athletesPrint方法写入数据到指定的文件
  • 7.4 测试结果和覆盖率
  • 8.异常处理
  • 8.1 main()函数参数异常
  • 8.2 input.txt内容异常
  • 8.3 org.json库和Java标准库相关异常
  • 9.心得体会

1.Gitcode项目地址

Gitcode项目地址

2.PSP表格

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

3.解题思路描述

3.1 选择解析类库

经过对比目前常用的解析类库使用方式和学习成本,最后我选择了学习成本较低但目前功能最全且无依赖的Google的Gson,并利用JsonParser类的实例进行后续的解析。

3.2 解析json文件

3.2.1 解析运动员信息数据(AthletesData)

首先对于运动员个人信息的解析,通过json文件我们可以注意到json文件的顶层结构是一个数组(包含多个国家的对象),因此将JsonElement对象转换为JsonArray对象。然后依次创建运动员列表、获取国家名称和参与信息、遍历参与信息数组、提取并存储运动员信息。

3.2.2 解析比赛信息数据(ResultData)

比赛信息的json文件相对于运行员信息文件涵盖信息较多,于是为了便于观察每个对象在哪个数组下,我将json文件做了简化(即将主要信息提取出来观察其相对于各个数组的位置)。然后, 从一个指定的JSON文件中解析数据,并将解析出的特定信息打印到给定的BufferedWriter输出流中,即进行了设置文件路径、读取和解析JSON文件、提取JSON对象、获取比赛数据、处理每个比赛(对于每个比赛,提取其JsonObject表示)、检查比赛名称、获取比赛结果、处理每个结果、提取和存储结果信息。

3.3 从命令行参数中获取的输入和输出文件路径(DWASearch)

DWASearch程序根据从命令行参数中获取的输入和输出文件路径,对输入文件进行读取和处理,并将结果写入输出文件。即进行了参数检查、文件读取和写入、逐行处理、字符串拆分和条件判断、错误处理、结束处理。

4.接口设计和实现过程

组织结构图

AthletesData类: 定义了一个名为athletesPrint的方法,该方法接受两个参数:output(输出路径)和out(一个BufferedWriter对象,用于写入输出)。
ResultData类: 定义了一个名为resultDataPrint的方法,该方法接受三个参数:outPut(输出路径的一部分),path(JSON文件的名称或路径的一部分)以及out(一个BufferedWriter对象,用于写入输出)。
DWASearch类: 循环读取input.txt每行的数据,判断指令的正确性。若指令正确则调用AthletesData类的athletesPrint方法或ResultData类的resultDataPrint方法,来进行选手信息的输出或比赛结果的输出;若指令不正确,则输出N/A或Error。
独到之处:实现了对不同数据源的解析和处理,并将结果输出到指定文件中,具有一定的通用性和可扩展性。

5.关键代码展示

1.对players信息进行JSON解析

try {
    JsonParser parser = new JsonParser();
    JsonElement element = parser.parse(new FileReader("D:\\软件工程实践\\project-java\\222100302\\src\\datas\\athletes.json"));
    //将JsonElement对象转换为 JsonArray,表示国家列表。
    JsonArray countries = element.getAsJsonArray();
    //创建一个新的 ArrayList,用于存储 Athlete 对象。
    List<Athlete> athletes = new ArrayList<>();

    for (JsonElement countryElement : countries) {
        JsonObject countryObject = countryElement.getAsJsonObject();
        String countryName = countryObject.get("CountryName").getAsString();
        JsonArray participations = countryObject.getAsJsonArray("Participations");

        for (JsonElement participationElement : participations) {
            //将 participationElement 转换为 JsonObject,代表一个运动员的参与信息。
            JsonObject participationObject = participationElement.getAsJsonObject();
            String firstName = participationObject.get("PreferredFirstName").getAsString();
            String lastName = participationObject.get("PreferredLastName").getAsString();
            int gender = participationObject.get("Gender").getAsInt();
            athletes.add(new Athlete(countryName, firstName, lastName, gender));
        }
    }

2.对决赛信息进行JSON解析

OutPutPath = outPut;
    String filePath = "D:/软件工程实践/project-java/222100302/src/datas/results/" + path + ".json";

    try {
        JsonParser parser = new JsonParser();//创建一个 JsonParser 实例,用于解析 JSON 数据。
        JsonElement element = parser.parse(new FileReader(filePath));

        JsonObject jsonObject = element.getAsJsonObject();
        JsonArray heats = jsonObject.getAsJsonArray("Heats");
        List<FinalResult> finalResult = new ArrayList<>();

        for (JsonElement heat : heats) {
            JsonObject heatObject = heat.getAsJsonObject();
            String name = heatObject.get("Name").getAsString();

            if (name.equals("Final")) {
                JsonArray results = heatObject.getAsJsonArray("Results");
                for (JsonElement result : results) {
                    JsonObject resultObject = result.getAsJsonObject();
                    String fullname = resultObject.get("FullName").getAsString();
                    int rank = resultObject.get("Rank").getAsInt();
                    JsonArray dives = resultObject.getAsJsonArray("Dives");

                    FinalResult fr = new FinalResult(fullname, rank);

                    StringBuilder divePointsStr = new StringBuilder("Score:");
                    float totalPoints = 0;

                    for (JsonElement dive : dives) {
                        JsonObject diveObject = dive.getAsJsonObject();
                        float divePoint = Float.parseFloat(diveObject.get("DivePoints").getAsString());
                        fr.divePoints.add(divePoint);
                        divePointsStr.append(String.format("%.2f", divePoint)).append(" + ");
                        totalPoints += divePoint;
                    }

                    if (divePointsStr.length() > 0) {
                        divePointsStr.setLength(divePointsStr.length() - 3);
                    }
                    divePointsStr.append(" = ").append(String.format("%.2f", totalPoints));
                    finalResult.add(fr);

                    out.write("Full Name: " + fr.fullName + "\n");
                    out.write("Rank: " + fr.rank + "\n");
                    out.write(divePointsStr + "\n");
                    out.write("-----\n");
                }
            }
        }
    }

3.在主函数中利用传入的字符串判断路径

try {
    BufferedReader bufferedReader = new BufferedReader(new FileReader(input));
    String str = bufferedReader.readLine();
    int len = str.length();
    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(output, true)));
    while (str != null) {
        //将input.txt的文件内容按空格拆分
        String[] str_sp = str.split(" ");
        if (str.equals("players")) {
            //如果是players,则调用AthletesData的athletesPrint方法将指定的运动员信息输出到output中
            AthletesData athletesData = new AthletesData();
            athletesData.athletesPrint(output, out);
        } else if (!str.equals("players") && str_sp.length == 1) {
            out.write("Error" + "\n");
            out.write("-----\n");
        } else if ("result".equals(str_sp[0])) {
            String newStr = str.substring(str_sp[0].length() + 1);
            String filePath = "D:/软件工程实践/project-java/222100302/src/datas/results/" + newStr + ".json";
            File file = new File(filePath);
            //判断该文件路径是否存在,存在即输出指定赛事信息
            if (file.exists()) {
                ResultData resultData = new ResultData();
                resultData.resultDataPrint(output, newStr, out);
            } else {
                out.write("N/A" + "\n");
                out.write("-----\n");
            }
        } else {
            out.write("Error" + "\n");
            out.write("-----\n");
        }
        str = bufferedReader.readLine();

    }
    bufferedReader.close();
    out.close();
} 

6.性能改进

写入文件时,由于FileWriter 没有内置的缓冲区,每次调用 write() 方法时都会直接写入文件。这会导致大量的 I/O 操作,尤其是在写入大量数据时,性能会显著下降。于是,我在方法athletesPrint(String output, BufferedWriter out)和方法resultDataPrint(String outPut, String path, BufferedWriter out)中使用BufferedWriter 替代原来的 FileWriter,进而改进程序的性能。

7.单元测试

7.1 main()方法测试

// 正常
 DWASearch.main(new String[]{"input.txt","output.txt"});
// 异常
 DWASearch.main(new String[]{"input.txt"});

main()方法测试展示

测试代码


测试结果

7.2 测试resultDataPrint方法写入数据到指定的文件

@Test
public void resultDataPrintTest0() {
    ResultData resultData = new ResultData();
    //正确写入
    String outPut = "output.txt";
    String path = "women 1m springboard";
    //错误写入
    String outPut = "output11111.txt";
    String path = "women 1m springboard";

    try {
        BufferedWriter writer = new BufferedWriter(new FileWriter(outPut));
        resultData.resultDataPrint(outPut, path, writer);
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

    assertEquals("output.txt", resultData.OutPutPath);
}

测试resultDataPrint方法写入数据到指定的文件结果展示
1.地址正确则可以写入文件

正确示例


正确示例


2.地址错误可见断言提示

地址错误断言提示

7.3 测试athletesPrint方法写入数据到指定的文件

@Test
public void athletesPrintTest0() {
    AthletesData athletesData = new AthletesData();
    BufferedWriter writer = null;
    try {
    //正确写入
        writer = new BufferedWriter(new FileWriter("output_test.txt"));
        athletesData.athletesPrint("output_test.txt", writer);
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

如7.2地址正确可以写入文件(这里将不再展示类似图片)

地址正确

7.4 测试结果和覆盖率

测试结果


覆盖率

8.异常处理

8.1 main()函数参数异常

public static void main(String[] args) throws IOException {
    if (!"input.txt".equals(args[0]) && !"output.txt".equals(args[1]) && (args.length!=2)) {
        System.out.println("Error!!");
    }

8.2 input.txt内容异常

 try {
    BufferedReader bufferedReader = new BufferedReader(new FileReader(input));
    String str = bufferedReader.readLine();
    int len = str.length();
    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(output, true)));
    while (str != null) {
        //将input.txt的文件内容按空格拆分
        String[] str_sp = str.split(" ");
        if (str.equals("players")) {
            //如果是players,则调用AthletesData的athletesPrint方法将指定的运动员信息输出到output中
            AthletesData athletesData = new AthletesData();
            athletesData.athletesPrint(output, out);
        } else if (!str.equals("players") && str_sp.length == 1) {
            out.write("Error" + "\n");
            out.write("-----\n");
        } else if ("result".equals(str_sp[0])) {
            String newStr = str.substring(str_sp[0].length() + 1);
            //否则,认为input.txt的内容暂时符合规则
            String filePath = "D:/软件工程实践/project-java/222100302/src/datas/results/" + newStr + ".json";
            File file = new File(filePath);    
            //判断该文件路径是否存在,存在即输出指定赛事信息
            if (file.exists()) {
                ResultData resultData = new ResultData();
                resultData.resultDataPrint(output, newStr, out);
            } else {
                out.write("N/A" + "\n");
                out.write("-----\n");
            }
        } else {
            out.write("Error" + "\n");
            out.write("-----\n");
        }
        str = bufferedReader.readLine();

    }
    bufferedReader.close();
    out.close();
} catch (Exception e) {
    e.printStackTrace();
}

8.3 org.json库和Java标准库相关异常

catch (JsonIOException e) {
        e.printStackTrace();
    } catch (JsonSyntaxException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

9.心得体会

在这次作业中,我学到了许多关于Java编程和JSON数据处理的知识,我在以下几个方面得到了提高:
熟练运用Gson库: 通过这个项目,我更加熟悉了如何使用Gson库来解析和处理JSON数据。我学会了如何使用JsonParser、JsonObject、JsonArray等类来操作JSON数据,以及如何处理异常情况。
文件读写操作: 在这个项目中,涉及了大量的文件读写操作,包括读取JSON文件、将结果输出到指定文件中等。这让我更加熟悉了Java中的文件操作方法,学会了使用BufferedReader、FileReader、BufferedWriter等类来进行文件读写。
排序和比较: 在AthletesData类中,实现了对运动员信息的排序功能。通过自定义Comparator来对Athlete对象进行比较和排序,实现了对国家名称和姓氏的二级排序。这让我对Java中的排序方法有了更深入的理解。
解决开发过程中遇到的难题时,我发现最具挑战性的部分是对JSON数据结构的理解和处理。需要深入理解JSON文件的结构,并且准确地获取到需要的数据。在处理JSON数据时,需要注意异常情况的处理,比如文件不存在、JSON格式错误等,这些需要特别小心和谨慎。
通过这个项目,我不仅提高了自己的编程能力和对于Gson库的应用水平,还加深了对Java文件操作和排序方法的理解。同时,也锻炼了自己分析问题、解决问题的能力。在未来的工作中,我将更加熟练地运用这些知识和经验,提高自己的编程水平,不断学习和进步。

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

122

社区成员

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

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