软工实践结对第二次作业
具体分工
- 陈晓彬
负责从CVPR网站爬取论文数据 - 陈璟
负责对论文数据进行分析
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | 40 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 40 |
Development | 开发 | 180 | 300 |
• Analysis | • 需求分析 (包括学习新技术) | 300 | 360 |
• Design Spec | • 生成设计文档 | 0 | 0 |
• Design Review | • 设计复审 | 10 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
• Design | • 具体设计 | 30 | 30 |
• Coding | • 具体编码 | 120 | 150 |
• Code Review | • 代码复审 | 10 | 20 |
• Test | • 测试(自我测试,修改代码,提交修改) | 10 | 20 |
Reporting | 报告 | 15 | 20 |
• Test Repor | • 测试报告 | 0 | 0 |
• Size Measurement | • 计算工作量 | 5 | 5 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 770 | 1025 |
由于以前没有用过Java语言,所以这次结对作业,我出力较少,只是学习了Java的爬虫,然后温习了一下Java的基本语法,为后面的项目做准备。
解题思路描述与设计实现说明
-
爬虫使用
我们访问某一个网页的时候,在地址栏输入网址,按回车,该网站的服务器就会返回一个HTML文件给我们,浏览器解析返回的数据,展示在UI上。同样爬虫程序也是模仿人的操作,给网站发送一个请求,网站会给爬虫程序返回一个HTML文件,爬虫程序再根据返回的数据进行抓取分析 。
Jsoup是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。首先Jsoup从一个URL,文件或字符串解析HTML,然后使用DOM或者CSS选择器来查找,取出数据。
-
代码组织与内部实现设计(类图)
代码分为两块,一块为Main
,这里是存放主函数的地方,负责对参数进行分析,对另一个类进行调用来分析文本以及输出结果,另一个是文本分析类FileParser
,这个类按照Main
给出的参数对文本进行分析,将分析出的词组或单词存放在一个map中,之后输出就可以用这个map中的数据进行输出
-
说明算法的关键与关键实现部分流程图
算法的关键在于对文本的分析,将其分为若干的词组。算法的关键在于状态的改变,可以吧本次作业理解成一个自动机,显而易见的是本次作业的输入文件是由论文编号和Title和Abstract组成的,其中Title和Abstract内容为一行字符串(这是观察发现的,如果不是就GG了),其中每行字符串可以理解为若干个字母数字组成的串以及若干个非字母数字组成的串交叉构成的,因此我设置了以下状态。
- 0
读取论文编号阶段 - 1
读取"Title: "阶段 - 2
读取Title中非字母数字串阶段 - 3
读取Title中字母数字串阶段 - 4
读取"Abstract: "阶段 - 5
读取Abstract中非字母数字串阶段 - 6
读取Abstract中字母数字串阶段 - 7
读取论文间两空格阶段
流程图如下所示
附加题设计与展示
-
设计的创意独到之处
-
实现思路
-
实现成果展示
关键代码解释
while((ch=br.read())!=-1) {
if(ch>255)continue;
//编号
if(state==0) {
if(isDigit(ch)) {
temp.append((char)ch);
}
else {
int no=Integer.parseInt(temp.toString());
//System.out.println(no);
line=(no+1)*2;
state=1;
temp.setLength(0);
}
}
//读取Title:
else if(state==1) {
if(ch==' ') {
cizu.setLength(0);
del.clear();
state=2;
start=false;
temp.setLength(0);
}
}
//非字母数字
else if(state==2) {
charNum++;
if(ch=='
') {
//under windows , delete the ' '
charNum--;
state=4;
}
if(isLetter(ch)||isDigit(ch)) {
if(m>1&&start) {
cizu.append(temp.toString());
del.offer(temp.length());
}
temp.setLength(0);
isWord=true;
pos=0;
state=3;
if(isDigit(ch)) {
isWord=false;
}
if(ch>='A'&&ch<='Z') {
ch-='A'-'a';
}
}
temp.append((char)ch);
}
//字母数字
else if(state==3) {
charNum++;
if(ch>='A'&&ch<='Z') {
ch-='A'-'a';
}
if(ch=='
') {
charNum--;
state=4;
}
if((!isLetter(ch)&&!isDigit(ch))) {
//not a word ,clear the cizu and del
if(!isWord||temp.length()<4) {
cizu.setLength(0);
del.clear();
}
//add word,if the queue is reach m,then add to map
else {
wordNum++;
start=true;
//add this word
cizu.append(temp.toString());
//add this size
del.offer(temp.length());
if(del.size()==m*2-1) {
if(mp.containsKey(cizu.toString())) {
int val=mp.get(cizu.toString())+val1;
mp.put(cizu.toString(), val);
}
else {
mp.put(cizu.toString(), val1);
}
int size=del.poll();
if(m>1) {
size+=del.poll();
}
cizu.delete(0, size);
}
}
if(state==4) {
}
else {
temp.setLength(0);
state=2;
}
}
temp.append((char)ch);
if(pos<4&&isDigit(ch)) {
isWord=false;
}
pos++;
}
//读取Abstract:
else if(state==4) {
if(ch==' ') {
cizu.setLength(0);
del.clear();
temp.setLength(0);
state=5;
start=false;
space=0;
}
}
//非字母数字
else if(state==5) {
charNum++;
if(ch=='
') {
//under windows , delete the ' '
charNum--;
state=7;
}
if(isLetter(ch)||isDigit(ch)) {
if(m>1&&start) {
cizu.append(temp.toString());
del.offer(temp.length());
}
temp.setLength(0);;
isWord=true;
pos=0;
state=6;
if(isDigit(ch)) {
isWord=false;
}
if(ch>='A'&&ch<='Z') {
ch-='A'-'a';
}
}
temp.append((char)ch);
}
//字母数字
else if(state==6) {
charNum++;
if(ch>='A'&&ch<='Z') {
ch-='A'-'a';
}
if(ch=='
') {
charNum--;
state=7;
}
if((!isLetter(ch)&&!isDigit(ch))) {
//not a word ,clear the cizu and del
if(!isWord||temp.length()<4) {
cizu.setLength(0);
del.clear();
}
//add word,if the queue is reach m,then add to map
else {
wordNum++;
start=true;
//add this word
cizu.append(temp.toString());
//add this size
del.offer(temp.length());
if(del.size()==m*2-1) {
if(mp.containsKey(cizu.toString())) {
int val=mp.get(cizu.toString())+val2;
mp.put(cizu.toString(), val);
}
else {
mp.put(cizu.toString(), val2);
}
int size=del.poll();
if(m>1) {
size+=del.poll();
}
cizu.delete(0, size);
}
}
if(state==7) {
}
else {
temp.setLength(0);
state=5;
}
}
temp.append((char)ch);
if(pos<4&&isDigit(ch)) {
isWord=false;
}
pos++;
}
//行间两空行
else if(state==7) {
if(ch=='
') {
space++;
}
if(space==2) {
state=0;
temp.setLength(0);
}
}
}
性能分析与改进
在本次作业中基本上没有在整体完成之后的大幅度性能改进,在编码过程中就有做一些性能上的调优,我是使用Java来实现本次作业的,比如说文件读入时我用的通过BufferedReader包装的FileReader而不是使用InputFIleStream,是因为使用具有缓冲区的BufferedReadder读取更快,在本次得到的论文列表测试中有肉眼可以见的进步,其次是将map的实现由TreeMap改为HashMap,TreeMap的内部实现为二叉树,而HashMap 的内部实现为哈希表,显而易见HashMap 的插入和查询的时间复杂度是要比TreeMap更优的。
本次程序的耗费时间较短,暂不需要性能优化
单元测试
public class FileParserTest {
public FileParser fp=new FileParser();
@Test
public void testParser() {
File file=new File("d://1.txt");
FileReader fr;
try {
fr = new FileReader(file);
fp.Parser(fr, 1, 1);
assertEquals(fp.getCharNum(),39);
assertEquals(fp.getLine(), 6);
assertEquals(fp.getWordNum(), 10);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
因为在一个函数中就把所有事都做了,所以构造了一个测试文本简单测试了一下
Github代码签入记录
本人代码是一次写完的,所以并没有很多的签入记录
遇到的代码模块异常及解决方法
学习一门新的语言,基本语法确实十分简单,容易上手,但是到了应用的时候,就会觉得手足无措。学一个爬虫,我就学了两天,真的对自己的菜感到无能为力。感谢璟哥愿意带我这个菜鸡。还有感谢一下守成同学,爬虫是他教我的。
编码的时候,那时候配环境配到自己绝望,从官网的11到10到7再到8,来来回回,下载,卸载,在崩溃的边缘疯狂试探,导入Jsoup包的时候,一直有个问题,import一直报错,然后把项目删了,重新来过就可以,这个原因至今还没弄懂。还有一个问题就是IDE工具的JDK最高就10的版本,我最开始下载11下来,编译过不了。好多坑啊...
评价你的队友
璟哥跟我说,如果你让我动了自己写爬虫的想法,那证明我真的不靠谱。但是还好,最后我还是完成了被分配的一小部分任务。
学习进度条
第n周 | 新增代码行 | 本周学习耗时 | 重要成长 |
6 | 100 | 5 | Java学习 |