作业基本信息
这个作业属于哪个课程 | 软件工程-23年春季学期 |
---|---|
这个作业要求在哪里 | 软件工程实践寒假作业 |
这个作业的目标 | 完成对澳大利亚网球公开赛相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序 |
其他参考文献 | 《构建之法》 |
目录:
1. Gitcode项目地址
2. PSP表格
3. 解题思路描述
3.1 问题1
3.2 问题2
4. 接口设计和实现过程
5. 关键代码展示
6.性能改进
7. 单元测试
8. 异常处理
9. 心得体会
1. Gitcode项目地址
我的仓库
2. PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 20 | 35 |
Development | 开发 | 1440 | 1600 |
• Analysis | • 需求分析 (包括学习新技术) | 40 | 30 |
• Design Spec | • 生成设计文档 | 60 | 60 |
• Design Review | • 设计复审 | 30 | 35 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 15 |
• Design | • 具体设计 | 160 | 100 |
• Coding | • 具体编码 | 180 | 200 |
• Code Review | • 代码复审 | 120 | 90 |
• Test | • 测试(自我测试,修改代码,提交修改) | 130 | 115 |
Reporting | 报告 | 180 | 120 |
• Test Repor | • 测试报告 | 90 | 70 |
• Size Measurement | • 计算工作量 | 50 | 60 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 40 |
合计 | 2610 | 2590 |
3. 解题思路描述
使用的json解析技术:org.json
org.json提供了一系列用于解析JSON文档的方法,可以使用其中包括:
getString():用于从JSON文档中获取字符串;
getInt():用于从JSON文档中获取整数;
getBoolean():用于从JSON文档中获取布尔值;
getJSONObject():用于从JSON文档中获取JSON对象;
getJSONArray():用于从JSON文档中获取JSON数组;
toString():用于将JSON文档转换为字符串;
toJSONObject():用于将JSON文档转换为JSON对象;
toJSONArray():用于将JSON文档转换为JSON数组。
3.1 问题1
思路:
如下是players.json的数据结构,所以只需要通过构造new JSONObject(jsonString)获得一个JSONObject 对象,然后通过 getJSONArray(“players”) 拿到一个存储着运动员信息的json数组,数组中存储的每一个json对象中就有我们需要拿到的各种信息。这时只需要调用getJSONObject()方法拿到一个json对象然后再通过getJSONxxx(“key”)方法就能拿到需要的信息了。
3.2 问题2
输出正式赛每日结果
思路:
如下图是存储着每日比赛的相关信息的json数据结构图,先拿到matchs、teams、players json数组,matchs中的每一个json对象都存储着一场比赛的相关信息,所以我们可以先拿到matchs然后再拿到其中的每一项数据然后再拿到每一场比赛的开始时间还有一个teams的数组,其中包含了这场比赛的分数胜者队伍teamId,然后再通过teamId到 teams数组中找到teamId相同的对象然后拿到其中的players数组中的player的Id,然后通过Id遍历players数组查找,最后拿到比赛获胜者的名字。
4. 接口设计和实现过
4.1 输出所有选手信息
首先获取players的json数组
String jsonString = new String(Files.readAllBytes(Paths.get(patnIn)));
// 将JSON文件解析为JSON对象
JSONObject jsonObject = new JSONObject(jsonString);
// 获取JSON数组
JSONArray jsonArray = jsonObject.getJSONArray("players");
然后遍历取出数据即可
// 遍历JSON数组
for (int i = 0; i < jsonArray.length(); i++) {
// 获取JSON对象
JSONObject playerObject = jsonArray.getJSONObject(i);
// 读取JSON对象中的属性
String fullName = playerObject.getString("full_name");
fw.write("full_name:"+fullName+"\n");
String gender = playerObject.getString("gender");
fw.write("gender:"+gender+"ale"+"\n");
String nationality = playerObject.getJSONObject("nationality").getString("name");
fw.write("nationality:"+nationality+"\n");
fw.write("-----\n");
}
4.2 输出正式赛每日结果
在获得matchs数组后,通过函数getStartTime()获取比赛开始的时间
public static void getStartTime(JSONObject match,FileWriter fw) throws IOException {
fw.write("time:"+match.getString("actual_start_time")+"\n");
}
通过函数showScore()可以获取比赛结果并输入到输出文件中
//分数
public static void showScore(JSONObject match,FileWriter fw) throws IOException {
JSONArray teams = match.getJSONArray("teams");
List<String> winnerScoreList = new ArrayList<>();
List<String> loserScoreList = new ArrayList<>();
int j;
if (teams.getJSONObject(0).has("status")){
JSONArray score = teams.getJSONObject(0).getJSONArray("score");
for ( j = 0; j < score.length(); j++) {
String game = score.getJSONObject(j).getString("game");
String game2 = teams.getJSONObject(1).getJSONArray("score").getJSONObject(j).getString("game");
winnerScoreList.add(game);
loserScoreList.add(game2);
}
} else{
JSONArray score = teams.getJSONObject(1).getJSONArray("score");
for (j = 0; j < score.length(); j++) {
String game = score.getJSONObject(j).getString("game");
String game2 = teams.getJSONObject(0).getJSONArray("score").getJSONObject(j).getString("game");
winnerScoreList.add(game);
loserScoreList.add(game2);
}
}
//输出
fw.write("score:");
for (int i = 0; i <= j-1; i++) {
fw.write(winnerScoreList.get(i)+":"+loserScoreList.get(i)+" ");
if(i+1<j) fw.write("| ");
}
fw.write("\n");
}
通过以下代码获取teamID然后通过teamID找到playerID最后使用getPlayer()函匹配运动员并获取名字
//获取teamId
public static String getTeamId(JSONObject match){
JSONArray teams = match.getJSONArray("teams");
String teamId="";
if (teams.getJSONObject(0).has("status")){
teamId = teams.getJSONObject(0).getString("team_id");
} else{
teamId = teams.getJSONObject(1).getString("team_id");
}
return teamId;
}
//查找playerId
public static List<String> getPlayerId(JSONArray teams,String teamId){
JSONArray tem;
List<String> playerList = new ArrayList<>();
for(int i=0;i<teams.length();i++)
{
if(teams.getJSONObject(i).getString("uuid").equals(teamId)){
tem=teams.getJSONObject(i).getJSONArray("players");
int l= tem.length();
for (int j = 0; j <l ; j++) {
playerList.add(tem.getString(j));
}
}
}
return playerList;
}
//获取运动员名字
public static List<String> getPlayer(JSONArray players, List<String> playerIdList){
List<String> playerList = new ArrayList<>();
for (int i = 0; i <playerIdList.size() ; i++) {
String pID=playerIdList.get(i);
for(int j=0;j<players.length();j++)
{
String uuid=players.getJSONObject(j).getString("uuid");
if(uuid.equals(pID)){
String pName=players.getJSONObject(j).getString("short_name");
playerList.add(pName);
}
}
}
return playerList;
}
5. 关键代码展示
读取运动员信息
public static String readToFile(String patnIn) throws IOException {
String jsonString = new String(Files.readAllBytes(Paths.get(patnIn)));
// 将JSON文件解析为JSON对象
JSONObject jsonObject = new JSONObject(jsonString);
// 获取JSON数组
JSONArray jsonArray = jsonObject.getJSONArray("players");
String contain ="";
// 遍历JSON数组
for (int i = 0; i < jsonArray.length(); i++) {
// 获取JSON对象
JSONObject playerObject = jsonArray.getJSONObject(i);
// 读取JSON对象中的属性
String fullName = playerObject.getString("full_name");
contain+="full_name:"+fullName+"\n";
String gender = playerObject.getString("gender");
if(gender.equals("F")){
contain+="gender:"+gender+"emale"+"\n";
}else{
contain+="gender:"+gender+"ale"+"\n";
}
String nationality = playerObject.getJSONObject("nationality").getString("name");
contain+="nationality:"+nationality+"\n";
contain+="-----\n";
}
return contain;
}
读取每日比赛结果
public static String readJsonToFile(String patnIn) throws IOException {
String jsonString = new String(Files.readAllBytes(Paths.get(patnIn)));
// 将JSON文件解析为JSON对象
JSONObject jsonObject = new JSONObject(jsonString);
// 获取matchesJSON
JSONArray matchesJsonA = jsonObject.getJSONArray("matches");
//获得team
JSONArray teams = jsonObject.getJSONArray("teams");
//获得player
JSONArray players = jsonObject.getJSONArray("players");
String contain = "";
for (int i = 0; i <matchesJsonA.length() ; i++) {
JSONObject match = matchesJsonA.getJSONObject(i);
//判断是否弃赛
if(match.getJSONObject("match_status").getString("abbr").equals("W/O")){
contain+="W/O\n"+"-----"+"\n";
continue;
}
contain+=getStartTime(match);
List<String> playerId = getPlayerId(teams, getTeamId(match));
List<String> playerName = getPlayer(players, playerId);
contain+=showWinner(playerName);
contain+=showScore(match);
contain+="-----"+"\n";
}
return contain;
}
private static String showWinner(List<String> players) throws IOException {
int size = players.size();
String contain="winner:";
for (int i = 0; i <size ; i++) {
contain+=players.get(i);
if(i+1<size) contain+=" & ";
}
contain+="\n";
return contain;
}
//比赛开始时间
private static String getStartTime(JSONObject match) throws IOException {
String contain="time:"+match.getString("actual_start_time")+"\n";
return contain;
}
//分数
private static String showScore(JSONObject match) throws IOException {
JSONArray teams = match.getJSONArray("teams");
List<String> winnerScoreList = new ArrayList<>();
List<String> loserScoreList = new ArrayList<>();
String contain="";
int j;
if (teams.getJSONObject(0).has("status")){
JSONArray score = teams.getJSONObject(0).getJSONArray("score");
for ( j = 0; j < score.length(); j++) {
String game = score.getJSONObject(j).getString("game");
String game2 = teams.getJSONObject(1).getJSONArray("score").getJSONObject(j).getString("game");
winnerScoreList.add(game);
loserScoreList.add(game2);
}
} else{
JSONArray score = teams.getJSONObject(1).getJSONArray("score");
for (j = 0; j < score.length(); j++) {
String game = score.getJSONObject(j).getString("game");
String game2 = teams.getJSONObject(0).getJSONArray("score").getJSONObject(j).getString("game");
winnerScoreList.add(game);
loserScoreList.add(game2);
}
}
//输出
contain+="score:";
for (int i = 0; i <= j-1; i++) {
contain+=winnerScoreList.get(i)+":"+loserScoreList.get(i)+" ";
if(i+1<j) contain+="| ";
}
contain+="\n";
return contain;
}
//获取teamId
private static String getTeamId(JSONObject match){
JSONArray teams = match.getJSONArray("teams");
String teamId="";
if (teams.getJSONObject(0).has("status")){
teamId = teams.getJSONObject(0).getString("team_id");
} else{
teamId = teams.getJSONObject(1).getString("team_id");
}
return teamId;
}
//查找playerId
private static List<String> getPlayerId(JSONArray teams,String teamId){
JSONArray tem;
List<String> playerList = new ArrayList<>();
for(int i=0;i<teams.length();i++)
{
if(teams.getJSONObject(i).getString("uuid").equals(teamId)){
tem=teams.getJSONObject(i).getJSONArray("players");
int l= tem.length();
for (int j = 0; j <l ; j++) {
playerList.add(tem.getString(j));
}
}
}
return playerList;
}
//获取运动员名字
private static List<String> getPlayer(JSONArray players, List<String> playerIdList){
List<String> playerList = new ArrayList<>();
for (int i = 0; i <playerIdList.size() ; i++) {
String pID=playerIdList.get(i);
for(int j=0;j<players.length();j++)
{
String uuid=players.getJSONObject(j).getString("uuid");
if(uuid.equals(pID)){
String pName=players.getJSONObject(j).getString("short_name");
playerList.add(pName);
}
}
}
return playerList;
}
6.性能改进
当input.txt里面的指令有很多重复指令时多次访问相同的内存就会浪费时间,所以我们可以用Hashma<>这种数据结构来当一个cache存储之前访问过的文件的数据。下次再访问到时就可以字节从cache中直接调用。改进如下:
public static void main(String[] args) {
BufferedReader reader = null;
HashMap<String,String> cache=new HashMap<>();//缓存
//读取指令
String line=null;
int l=args.length;//参数个数
if(l!=2) {
System.out.println("参数输入有误");
try {
throw new IOException("参数必须是2个");
} catch (IOException e) {
e.printStackTrace();
}
}else if(!args[0].equals("input.txt")|!args[1].equals("output.txt")){
try {
throw new IOException("参数输入有误");
} catch (IOException e) {
e.printStackTrace();
}
}
else{
try {
reader = new BufferedReader(new FileReader(args[0]));
String contain="";
FileWriter fw;
try {
fw = new FileWriter(new File(args[1]),true);
} catch (IOException e) {
e.printStackTrace();
return;
}
while((line=reader.readLine())!=null){
//判断是否命中缓存
if(cache.containsKey(line)){
fw.write(cache.get(line));
}else{
String realpath=null;//真是路径
if(line.equals("players")){
realpath=path+line+".json";
try {
contain=RPToFile.readToFile(realpath);
fw.write(contain);
fw.flush();
cache.put(line,contain);//存入缓存
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件找不到");
}
}else{
String[] s = line.trim().split(" ");
realpath=path+s[1]+".json";
try {
contain=RMRToFile.readJsonToFile(realpath);
fw.write(contain);
fw.flush();
cache.put(line,contain);//存入缓存
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件找不到");
}
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
改进前运行相同指令(300+)对比:
7. 单元测试
主要对几个主要的函数进行测试
public class AOSearchTest {
@Test
public void testASOearch() {
AOSearch.main(new String[]{"input.txt", "output.txt"});
}
@Test
public void cacheTest(){
HashMap<String,String> cache =new HashMap<>();
}
//测试readToFile()函数
@Test
public void test1() throws IOException {
System.out.println(RPToFile.readToFile("src\\main\\data\\players.json"));
}
//测试readJsonToFileP()函数
@Test
public void test2() throws IOException {
System.out.println(RMRToFile.readJsonToFile("src\\main\\data\\0116.json"));
}
//测试检测command输入指令的正确与否
@Test
public void testJugeCommand() throws IOException {
ArrayList<String> list=new ArrayList<>();
list.add("players");
list.add("0116");
list.add("0117");
list.add("0118");
list.add("0119");
list.add("0120");
list.add("0121");
list.add("0122");
list.add("0123");
list.add("0124");
list.add("0125");
list.add("0126");
list.add("0127");
list.add("0128");
list.add("0129");
list.add("Q1");
list.add("Q2");
list.add("Q3");
list.add("Q4");
System.out.println(new AOSearch().jugeCommand("result",list));
}
}
8. 异常处理
异常处理主要是数据文件找不到的异常以及输入的参数有问题而主动抛出的异常,主要是为了提醒用户输入参数规范
HashMap<String,String> cache=new HashMap<>();//缓存
//读取指令
String line=null;
int l=args.length;//参数个数
if(l!=2) {
System.out.println("参数输入有误");
try {
throw new IOException("参数必须是2个");
} catch (IOException e) {
e.printStackTrace();
}
}else if(!args[0].equals("input.txt")|!args[1].equals("output.txt")){
try {
throw new IOException("参数输入有误");
} catch (IOException e) {
e.printStackTrace();
}
}
else{
try {
reader = new BufferedReader(new FileReader("src\\main\\java\\hxl\\main\\"+args[0]));
String contain="";
FileWriter fw;
try {
fw = new FileWriter(new File(args[1]),true);
} catch (IOException e) {
e.printStackTrace();
return;
}
while((line=reader.readLine())!=null){
//判断是否命中缓存
if(cache.containsKey(line)){
fw.write(cache.get(line));
}else{
String realpath=null;//真是路径
if(line.trim().equals("players")){
realpath=path+line+".json";
try {
contain=RPToFile.readToFile(realpath);
fw.write(contain);
fw.flush();
cache.put(line,contain);//存入缓存
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件找不到");
}
}else{
if(jugeCommand(line,list)==2) {
fw.write("ERROR\n" + "-----\n");
continue;
}
if (jugeCommand(line,list)==5){
String[] s = line.trim().split(" ");
realpath=path+s[1]+".json";
try {
contain=RMRToFile.readJsonToFile(realpath);
fw.write(contain);
fw.flush();
cache.put(line,contain);//存入缓存
} catch (IOException e) {
e.printStackTrace();
}
continue;
}
if(jugeCommand(line,list)==3){
fw.write("N/A\n"+"-----\n");
continue;
}
if(jugeCommand(line,list)==4){
continue;
}
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
9.心得体会
在这一次实践中,我尝试使用Java的org.json库来解析JSON文件。在开始之前,我首先阅读了JSON.org的文档,以便了解JSON的基本结构,并了解如何使用org.json库来解析JSON文件。随后,我开始编写代码,使用org.json库中的方法来解析JSON文件。在实践中,我发现org.json库提供了一系列用于解析JSON文档的方法,其中包括:getString()、getInt()、getBoolean()、getJSONObject()、getJSONArray()、toString()、toJSONObject()和toJSONArray()。这些方法使我能够轻松解析JSON文件,从而获取所需的信息。
通过这次实践,我学会了如何使用org.json库来解析JSON文件,并且了解了JSON文件的基本结构。我认识到,JSON是一种非常有用的数据格式,可以用于存储和传输数据,并且可以使用org.json库来解析JSON文件,从而获取所需的信息。