122
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 2302软件工程 |
|---|---|
| 这个作业要求在哪里 | 软件工程第二次作业--文件读取 |
| 这个作业的目标 | 完成对世界游泳锦标赛跳水项目相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序。 |
| 其他参考文献 | 《阿里巴巴Java开发手册》、《构建之法》 |
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 60 | 60 |
| • Estimate | • 估计这个任务需要多少时间 | 60 | 60 |
| Development | 开发 | 1610 | 2025 |
| • Analysis | • 需求分析 (包括学习新技术) | 180 | 360 |
| • Design Spec | • 生成设计文档 | 20 | 20 |
| • Design Review | • 设计复审 | 10 | 10 |
| • Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 15 |
| • Design | • 具体设计 | 30 | 50 |
| • Coding | • 具体编码 | 1200 | 1300 |
| • Code Review | • 代码复审 | 30 | 30 |
| • Test | • 测试(自我测试,修改代码,提交修改) | 120 | 240 |
| Reporting | 报告 | 100 | 120 |
| • Test Repor | • 测试报告 | 60 | 50 |
| • Size Measurement | • 计算工作量 | 20 | 20 |
| • Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
| 合计 | 1770 | 2205 |
经过对比目前常用的解析类库使用方式和学习成本,最后我选择了学习成本较低但目前功能最全且无依赖的Google的Gson,并利用JsonParser类的实例进行后续的解析。
首先对于运动员个人信息的解析,通过json文件我们可以注意到json文件的顶层结构是一个数组(包含多个国家的对象),因此将JsonElement对象转换为JsonArray对象。然后依次创建运动员列表、获取国家名称和参与信息、遍历参与信息数组、提取并存储运动员信息。
比赛信息的json文件相对于运行员信息文件涵盖信息较多,于是为了便于观察每个对象在哪个数组下,我将json文件做了简化(即将主要信息提取出来观察其相对于各个数组的位置)。然后, 从一个指定的JSON文件中解析数据,并将解析出的特定信息打印到给定的BufferedWriter输出流中,即进行了设置文件路径、读取和解析JSON文件、提取JSON对象、获取比赛数据、处理每个比赛(对于每个比赛,提取其JsonObject表示)、检查比赛名称、获取比赛结果、处理每个结果、提取和存储结果信息。
DWASearch程序根据从命令行参数中获取的输入和输出文件路径,对输入文件进行读取和处理,并将结果写入输出文件。即进行了参数检查、文件读取和写入、逐行处理、字符串拆分和条件判断、错误处理、结束处理。

AthletesData类: 定义了一个名为athletesPrint的方法,该方法接受两个参数:output(输出路径)和out(一个BufferedWriter对象,用于写入输出)。
ResultData类: 定义了一个名为resultDataPrint的方法,该方法接受三个参数:outPut(输出路径的一部分),path(JSON文件的名称或路径的一部分)以及out(一个BufferedWriter对象,用于写入输出)。
DWASearch类: 循环读取input.txt每行的数据,判断指令的正确性。若指令正确则调用AthletesData类的athletesPrint方法或ResultData类的resultDataPrint方法,来进行选手信息的输出或比赛结果的输出;若指令不正确,则输出N/A或Error。
独到之处:实现了对不同数据源的解析和处理,并将结果输出到指定文件中,具有一定的通用性和可扩展性。
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();
}
写入文件时,由于FileWriter 没有内置的缓冲区,每次调用 write() 方法时都会直接写入文件。这会导致大量的 I/O 操作,尤其是在写入大量数据时,性能会显著下降。于是,我在方法athletesPrint(String output, BufferedWriter out)和方法resultDataPrint(String outPut, String path, BufferedWriter out)中使用BufferedWriter 替代原来的 FileWriter,进而改进程序的性能。
// 正常
DWASearch.main(new String[]{"input.txt","output.txt"});
// 异常
DWASearch.main(new String[]{"input.txt"});
main()方法测试展示


@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.地址正确则可以写入文件



@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地址正确可以写入文件(这里将不再展示类似图片)



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