142
社区成员




这个作业属于哪个课程 | 2022年福大-软件工程、实践-W班 |
---|---|
这个作业要求在哪里 | 软件工程实践第二次作业——个人实战 |
这个作业的目标 | 完成对冬奥会的赛事数据的收集,并实现一个能够对国家排名及奖牌个数统计的控制台程序。 |
其他参考文献 | Fastjson 简明教程 |
目录
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 单元格 | 30 | 35 |
• Estimate | • 估计这个任务需要多少时间 | 15 | 20 |
Development | • 开发 | 480 | 700 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 100 |
• Design Spec | • 生成设计文档 | 30 | 40 |
• Design Review | • 设计复审 | 30 | 30 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 40 |
• Design | • 具体设计 | 90 | 50 |
• Coding | • 具体编码 | 360 | 550 |
• Code Review | • 代码复审 | 30 | 30 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 120 |
Reporting | 报告 | 60 | 60 |
• Test Repor | 测试报告 | 30 | 20 |
• Size Measurement | • 计算工作量 | 30 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 20 |
• 总计 | 1055 | 1255 |
【该爬取行为仅用于课程教学】
在东奥专栏打开每日赛程,在浏览器用F12打开开发者工具,选择网络按钮,点击对应的日期
分析请求标头可以得到发送GET请求的链接
爬取数据我使用python里的requests包的get(url)方法读出response,
将用response.text()读出返回文本数据r
用r[3:-2]掐头去尾,并且将r字符串中的"\/"替换为"/"
然后利用r.encode("utf8").decode('unicode_escape')语句将r从Unicode编码转为utf8
最后写入data文件夹中
代码如图
编写两个实体类Medal和Match用来分别接收奖牌和比赛数据
利用第三方jar包fastjson进行解析得到Medal列表如下,Match也是类似
利用输入流将input的指令全部读入
利用String类的split("\\\n")方法将读入的字符串变成String列表
将指令首尾空格去掉后判断首字符是否为t或s,否的话输出Error
否则进一步判断是否是total,如是输出总榜单,不是则判断前面八个字符是否是schedule,否则输出Error,是的话判断后面是否带有空格加指定范围内的日期,是则输出对应日期的比赛列表,否则输出N/A
public class OlympicSearch {}
//调用Lib类的函数从input.txt中读取指令,验证指令,根据指令输出选择对应输出
public class Lib {}
//Lib类,提供所有的工具函数,包括读取文件函数,读取指令函数,写入文件函数,json解析函数,输出格式控制函数,验证指令函数
public class Match {}
//用来存放从json数据读取的赛事信息,位于pojo软件包下
public class Medal {}
//用来存放从json数据读取的奖牌榜信息,位于pojo软件包下
OlympicSearch类
public static void main(String[] args) {}
//程序入口,调用函数,读入指令,并且进行判断及输出
Lib类
public String readFile(String fileName){}
//读取data下的json文件
public String readInputTxt(String fileName){
//读取input.txt文件
public void outputFile(String fileName,String content){}
//将指定内容输出对应文件
public void deleteFile(String fileName){}
//删除指定文件
public List<String> getInstructionList(String fileName){}
//从input.txt文件读取指令并将其转换为List<String>类型
public List<Medal> readTotal(String totalFIle){}
//读取total.json文件并将其解析为List<Medal>
public List<Match> readMatch(String matchFile){}
//读取schedule XXXX.json文件并将其解析为List<Match>
public String getOutputTotal(List<Medal> medalList){}
//List<Medal>类型的列表转换成题目要求输出格式
public String getOutPutMatch(List<Match> matchList){}
//List<Match>类型的列表转换成题目要求输出格式
public String validateInstruction(String instruction){}
//验证指令
代码开始,如果给定的输出文件已存在,先删除,
然后遍历指令,验证指令,按照对应指令得到对应输出值,拼接到存放文件输出字符串outputContent中,
拼接一定数量后outputContent输出到指定输出文件,
对于合法指令,先判断是否在hashMap中存在,存在则直接从hashMap中取
不存在则将其放到hashmap中
Lib lib = new Lib();
if (!lib.deleteFile(outputFile)) {
return;
}
Map<String,String> map = new HashMap<>();
StringBuilder outputContent = new StringBuilder("");
List<String> instructionList = lib.getInstructionList(inputFile);
int cnt = 0;
for (String instruction : instructionList) {
cnt++;
String validateResult = lib.validateInstruction(instruction);
if (validateResult.equals("total")) {
String outputTotal = "";
if (map.containsKey("total")) {
outputTotal = map.get("total");
}
else {
List<Medal> medalList = lib.readTotal(totalFIle);
outputTotal = lib.getOutputTotal(medalList);
map.put("total",outputTotal);
}
outputContent.append(outputTotal);
}
else if (validateResult.equals("Error")||validateResult.equals("N/A")) {
outputContent .append(validateResult).append("\n-----\n");
}
else {
String date = validateResult;
String outputMatch = null;
if (map.containsKey(date)) {
outputMatch = map.get(date);
}
else {
outputMatch = lib.getOutPutMatch(lib.readMatch(matchFilePrefix+date+".json"));
map.put(date,outputMatch);
}
outputContent.append(outputMatch);
}
if (cnt > 10000 ) {
lib.outputFile(outputFile,outputContent.toString());
outputContent = new StringBuilder("");
cnt = 0;
}
}
lib.outputFile(outputFile,outputContent.toString());
将输入指令全部读入,然后利用split("\\\n")分割出每一行,
然后将分割出的每行用strip()将收尾空格去除,如果结果为空则说明此行为空行,跳过
否则放到String列表中,最后将String列表返回。
public List<String> getInstructionList(String fileName) {
List<String> instructionList = new ArrayList<>();
String content = readInputTxt(fileName);
for (String s : content.split("\\\n")) {
s=s.strip();
if (!s.equals(""))
instructionList.add(s);
}
return instructionList;
}
参数传入已去除首尾空格的指令列表
初始化验证结果为Error,判断指令首字母是否为t或者s,
如果为t,判断其是否是total,是则返回total,否则返回Error
如果为s判断指令长度是否小于12或者前八个字符是否为schedule,
如果是则返回Error,否则进一步判断schedule后面是有空格
无则返回Error,有则进一步判断其是否符合指定的日期格式
否则返回N/A,是则返回对应的日期
最后特判一下指令是schedule这种情况,是则返回N/A
public String validateInstruction(String instruction) {
instruction = instruction.strip();
String validateAns = "Error";
if (instruction.substring(0,1).equals("t")) {
if (instruction.length()!=5||(!instruction.equals("total")))
validateAns = "Error";
else
validateAns = "total";
}
else if(instruction.substring(0,1).equals("s")) {
if (instruction.length()<12||(!instruction.substring(0,8).equals("schedule")))
validateAns = "Error";
else {
String date = instruction.substring(8,instruction.length());
if (!date.substring(0,1).equals(" "))
validateAns = "Error";
else {
date = date.strip();
if (date.length()!=4 || (!date.matches("\\d\\d\\d\\d")))
validateAns = "N/A";
else if (!((date.matches("020[2-9]"))||date.matches("021[0-9]")||date.matches("0220")))
validateAns = "N/A" ;
else
validateAns = date;
}
}
if (instruction.equals("schedule"))
validateAns = "N/A";
}
return validateAns;
}
用一百万行有效指令测试,跑了四十几分钟都停不下来,内存最高消耗4、5G,写了个3A大作,电脑原地起飞
经过对各部分代码的时间测速,我发现每次从json文件读入进行解析以及String的拼接耗费时间大,于是我用hashmap将已访问过的数据储存到内存,若重复访问时则直接到hashmap取出,
并且对于次数很高的字符串拼接不采用String+String的形式,而采用StringBuilder.append()。
并且将输出数据拼接到一定大小才输出,而不是一读指令就输出。
时间变成11秒,内存消耗变成两三百M
主要对验证函数进行测试
覆盖率测试
输入流不支持相应编码时会出错,这个异常在读入文件并指定编码时可能会出现
文件读写可能会出现异常
文件找不到异常,指定读写的文件时可能会出现该异常
不知道怎么开始做作业的时候,可以先写个大概的博客,确定大致的步骤
遇到问题不过分盲目调试,应学会百度
应学会如何定位问题,学会测试代码各个部分的大致运行时间进行优化
应一开始思考大致的写法,应避免在后期再大改特改
不要过分相信自己的时间预估能力,应多安排点时间灵活机动写作业