博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
结对第二次—文献摘要热词统计及进阶需求
阅读量:6926 次
发布时间:2019-06-27

本文共 16767 字,大约阅读时间需要 55 分钟。

课程链接:

作业要求:
结对学号: |
作业目标1:一、基本需求:实现一个能够对文本文件中的单词的词频进行统计的控制台程序。
作业目标2:二、进阶需求:在基本需求实现的基础上,编码实现顶会热词统计器

团队分工:

黄权焕:

1.主要代码实现
2.需求分析讨论
3.博客撰写
4.代码测试

陈红宝:

1.爬虫代码实现
2.需求分析讨论
3.博客撰写
4.代码测试

作业正文

一、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟)
Planning 计划
• Estimate • 估计这个任务需要多少时间 900
Development 开发
• Analysis • 需求分析 (包括学习新技术) 120
• Design Spec • 生成设计文档 60
• Design Review • 设计复审 30
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 30
• Design • 具体设计 120
• Coding • 具体编码 600
• Code Review • 代码复审
• Test • 测试(自我测试,修改代码,提交修改) 30
Reporting 报告 60
• Test Repor • 测试报告 30
• Size Measurement • 计算工作量 30
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 30
合计 1110 2210

二、解题思路描述

说太多的解题思路描述,反倒像是事后诸葛亮。我就着实的描述一下当时的情况。

记得开始工作的时候是上周六(3月9号)。周五晚上发布作业,大概看了一下需求。从感觉上来看,有些多,也有些复杂。

于是,周六早上和下午完成其他工作后,就开始讨论本次作业了。
但是,问题发生了!我的Eclipse用不了,要重新配置环境变量。跟着百度上的教程,一步一步的走,但总是解决一个错误又出现另外一个错误。期间,环境变量的配置方式都用了三种。花费一个晚上的时间,最后所有东西都卸载重装,问题终于解决了!顿时松了一口气。
但问题是,我的同伴,在这个晚上,已经把基础需求代码全部写出来了。
就像那句话怎么说:时间是讲公平的,当你忙于一件事情的时候,有人已经把另外一件事情忙完了!
有些惭愧,因为没有太多的讨论,以及对他有任何帮助!他一个人默默的完成了!
之后的一天也就是上周日(3月10号)晚上。我做的事情是理解同伴编写的代码。同时也理解作业的需求。
有很多收获,也发现了一些问题,之后会一一道来。

三、设计实现过程

基础需求实现

(1)需求分析

第一步、实现基本功能

1.统计文件的字符数:

- 只需要统计Ascii码,汉字不需考虑

- 空格,水平制表符,换行符,均算字符

2.统计文件的单词总数,单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。

英文字母: A-Z,a-z

字母数字符号:A-Z, a-z,0-9

分割符:空格,非字母数字符号

例:file123是一个单词,123file不是一个单词。file,File和FILE是同一个单词

3.统计文件的有效行数:任何包含非空白字符的行,都需要统计。

4.统计文件中各单词的出现次数,最终只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。

5.按照字典序输出到文件result.txt:例如,windows95,windows98和windows2000同时出现时,则先输出windows2000

输出的单词统一为小写格式

分析:从宏观上来说,就是输入一个文件的内容,然后截取以4个英文字母开头及以上的单词,输出。并统计单词总数,有效行数,各单词的出现次数。

然后,细分下来就是实现了!

实现讲解:

1.从文件中读取字符流到缓冲区,判断缓冲区的每一行字符,切割字符串(去掉非字母非数字的符号),将切割出来的字符串保存到字符数组中,并记录有效行

2.将字符数组中的字符串转换为小写格式(方便之后判断和输出)。循环判断字数组符中的字符串前4个字符是否是小写字母。如果是,则保存到哈希表中(方便字典序排序),并增加单词数。如果不是,则舍去该字符串。
3.有哈希表中存储的字符串,可以方便的输出单词和词频。
4.算法思路:
获取行数
将文件打开后,用readLine()函数逐行读取文本内容并保存在fContent上,此时叠加行数。

获取字符数

将字符串fContent读取成功后,字符数+=fContent.length;

获取单词数

将fContent使用split(“\W+”)分割成只有可写字符的单词组存入String [] ch中,单词数+=ch.length;

数据结构

使用HashMap保存单词和使用频率,不使用TreeMap的原因是,TreeMap没有自带按值排序后,相同值按字典序排序的特性。而HashMap可以使存储、查找的时间效率都在O(1)内完成,而不是TreeMap的log(N);
值得注意的是,Map本身排序需要转化成List,排序成功后因将结果应重新转化为LinkedHashMap。LinkedHashMap可以按插入顺序保存。

进阶需求

自定义输入输出

在类中额外保存输入输出名即可。

自定义词频统计输出

在类中额外保存一个最大单词数用来控制LinkedHashMap长度即可。

权重分析

在HashMap插值时,额外判断是否来自Title,是的话记录数+10,否则+1即可。

多参数的混合使用

读取一行,依旧用split(“-“)函数分割成不同指令,分别调用函数即可。

从文件中得到字符串

public void getWord()    {        LinkedHashMap
list = sortMap(maxWordNum); try { FileOutputStream fos = new FileOutputStream(fileOutput); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter buff = new BufferedWriter(osw); String content = "characters: " + fByteCount + "\r\n"; content += "words: "+ getFWordCount() + "\r\n"; content += "lines: "+ fRowCount + "\r\n"; Iterator
iterator = list.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); content += "<" + key.replace("=", ">: ") + "\r\n"; //System.out.println(key.replace("=", ">: ")); } //System.out.print(content); buff.write(content); buff.flush(); fos.close(); } catch (FileNotFoundException e) { // TODO e.printStackTrace(); } catch (IOException e) { // TODO e.printStackTrace(); } }
public void setWord()    {        try {            String fContent = "";            FileInputStream fis = new FileInputStream(fileInput);            InputStreamReader isr = new InputStreamReader(fis);            BufferedReader br = new BufferedReader(isr);            fWordCount = fByteCount = fRowCount = 0;            while ((fContent = br.readLine()) != null) {                if(fContent.length() > 3)//                {                    fRowCount ++;                    if(fContent.charAt(0) == 'T')                    {                        fContent = fContent.substring(6, fContent.length()-1 );                        fByteCount += fContent.length();//                        setMap(fContent,true);                    }                    else if(fContent.charAt(0) == 'A')                    {                        fContent = fContent.substring(9, fContent.length()-1 );//remove(Abstract: )                         fByteCount += fContent.length();//                        setMap(fContent,false);                    }                }            }            fis.close();        } catch (FileNotFoundException e) {            System.out.print("");            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }

判断字符是否符合要求

public void setMap(String fContent,boolean isTitle)    {        String [] ch = fContent.split("\\W+");        for(int i = 0; i< ch.length ;i++)        {            if(ch[i].length()>=4)            {                ch[i] = ch[i].toLowerCase();                if (isLower(ch[i].charAt(0)) && isLower(ch[i].charAt(1)) && isLower(ch[i].charAt(2)) && isLower(ch[i].charAt(3)) )                {                    //System.out.print(ch[i]);                    fWordCount ++;                    if( map.containsKey(ch[i]) )                        map.put(ch[i],(wValue & isTitle) ? map.get(ch[i])+10 : map.get(ch[i])+1);                    else                         map.put(ch[i], (wValue & isTitle) ? 10 : 1);                }            }        }    }

哈希表排序(方便输出字典序)

public LinkedHashMap
sortMap(int num) { List
> list = new ArrayList
>(map.entrySet()); Collections.sort(list, new Comparator
>() { public int compare(Map.Entry
o1, Map.Entry
o2) { return o2.getValue() != o1.getValue() ? (o2.getValue() - o1.getValue()) : (o1.getKey()).toString().compareTo(o2.getKey()); //return (o1.getKey()).toString().compareTo(o2.getKey()); } }); LinkedHashMap
tmp = new LinkedHashMap
(); for (int i = 0; i < list.size() && i< num; i++) { String id = list.get(i).toString(); Integer value = list.get(i).getValue(); tmp.put(id, value); //System.out.println(id + (value)); } return tmp; }

代码优化和接口封装是同伴细化处理的,一眼看去就工整清秀。

public lib()    {        fileInput = "cvpr/result.txt";    }    public lib(String fName)    {        fileInput = fName;        setWord();    }    public void setFileInput(String fName)    {        fileInput = fName;    }    public void setFileOutput(String fName)    {        fileOutput = fName;    }    public void setWValue(int num)    {        wValue = num > 0 ? true : false;    }    public void setMaxWordNum(int num)    {        if(num >= 0)    maxWordNum = num;    }    public int getFWordCount()    {        return fWordCount;    }    public int getFRowCount()    {        return fRowCount;    }    public int getfByteCount()    {        return fByteCount;    }    public int getMaxWordNum()    {        return maxWordNum;    }    public int getMaxWordNum(int num)    {        return maxWordNum = num;    }    public boolean isLower(char c)    {        return (c>='a' && c<='z');    }    public boolean isDigit(char c)    {        return c<='0' && c<='9';    }

爬虫实现

爬虫实现是我自己一步一步的,从没听说过,到查资料,写代码,最后是输出 result.txt文件,用了三个晚上加一个通宵,和其他一些零零散散的时间。

怎么说呢!还是先描述一下学习的过程吧!因为用得时间多,学得也多嘛!
周一(3月11日)晚上,开始学习爬虫之旅。有些惶恐,也有些期待。当时觉得这个爬虫技术学会了,以后会有大用处。就像一个朋友开玩笑说:“学了爬虫,以后都可以自己上起点小说网爬取小说内容了!”
我记得一开始百度搜索的时候,爬虫软件很多,我下载了一个叫“后羿”的爬虫软件,不过没用上,因为要学习后自己编码!
学习的过程,大多是重复历史。从网上找一些具体的代码例子,看看别人怎么写代码,又实现了哪些功能。
而我学习的过程,借鉴了五份代码。都是一份一份的从网上查找,选取感觉适合的。然后一边敲写,一边理解。
第一份:关于URL链接输出HTML全部信息。从这份中,我学到了URL链接以及获取网页html的内容。
第二份:从一段字符串中,使用正则表达式截取电话号码。(当时还不知道正则表达式,只记得搜索相关资料的时候看见网上有人表示:“正则表达式写错了一点点,后面就全部错了。”于是,谨慎细微,不敢擅自修改样例中的正则表达式。但最后在自己理解正则表达式后发现,这好像也不是当时想的那么难的。)
第三份:从单个网页中截取需要的信息(用正则表达式)。当时在里的时候,就是很谨慎的,正则表达式和对应网页上的信息看了一遍又一遍,勉强理解了,但几乎不会用。直到把正则表达式的规则看了许多遍,才豁然开朗。
第四份:是关于读取从第一个页面出发,然后读取很多页面的链接以及链接里的内容。这份样例代码不多,但当时理解起来很繁琐,头都要炸了
第五份:和第三份一样,是从单个网页中截取需要的信息。我最后完成的代码,大部分格式都是模仿这一份的。
完成篇:代码爬取出论文后,依旧还有两个改进,这个下面优化的时候会讲。

爬虫实现:

1.获取一个页面上的标题和摘要信息

private static String  htmlFiter(String html,int num)         {            StringBuffer buffer = new StringBuffer();                      String str1= "";            String str2= "";                            // 匹配Title(题目),题目被包含在
中 Pattern p1 = Pattern.compile("(
)(.+?)(
*)"); Matcher m1 = p1.matcher(html); // 匹配Abstract(摘要),摘要被包含在
中 Pattern p2 = Pattern.compile("(\"abstract\")(.+?)(*)"); Matcher m2 = p2.matcher(html); if(m1.find() && m2.find()) { str1 = m1.group(2); str1 = num+"\r\n"+"Title: "+str1+"\r\n"; // buffer.append("\nTitle: "); // buffer.append(str1); str2 = m2.group(2); str2 = str2.replace(">",""); str2 = "Abstract:"+str2+"\r\n"+"\r\n"; // buffer.append("\nAbstract: "); // buffer.append(str2); // buffer.append("\n"); try { File file = new File("D:\\result.txt"); FileWriter fw = new FileWriter(file,true); String str5 = str1; fw.write(str5); String str6 = str2 + System.getProperty("line.separator"); fw.write(str6); fw.close(); }catch(Exception e) { e.printStackTrace(); } } return buffer.toString(); }

2.获取主页上论文的链接信息。调用第一个类,接入链接信息,爬取页面上的标题和摘要信息。并保存到result.txt文档中

private static String  htmlFiter(String html,int num)         {            StringBuffer buffer = new StringBuffer();            pre_2 p2 = new pre_2();                        String str = null;            Pattern p = Pattern.compile("(
)(.*)(
*)"); Matcher m = p.matcher(html); if(m.find()) { str = m.group(2); String str1 = null; String str2 = null; Pattern p1 = Pattern.compile("(class=\"ptitle\">)(.+?)(a href=\")(content_cvpr_2018)(.+?)(_2018_paper.html\">)"); Matcher m1 = p1.matcher(str); while(m1.find()) { str1 = m1.group(5); str2 = ("http://openaccess.thecvf.com/content_cvpr_2018"+str1+"_2018_paper.html"); buffer.append(str2); buffer.append("\n"); p2.getTodayTemperatureInfo(str2,num); num++; } } return buffer.toString(); }

3.主函数

public class pre_main {    pre_1 p1 = new pre_1();    String info = p1.getTodayTemperatureInfo("http://openaccess.thecvf.com/CVPR2018.py");}

感言:

就像一道门,在你走进去之前,你会胡乱猜测甚至害怕。因为你不知道门里面是什么,里面对于你来说是黑漆漆的一片。而人生来就对未知的东西有着期待和恐惧。

待你真正走进这道门的时候,你感叹一声:“原来如此。”
当时我完成截取论文的爬虫代码的时候,用了三个晚上加一个通宵。但现在,你让我再爬取难度相似的文档时,我只要十分钟。这大概就是师傅领进门的重要性吧!

四、改进的思路

(1)当时,我开始完成爬虫代码的时候,一共有两个程序。第一个:爬取主页中,关于论文链接的链接地址,保存到一个名叫 1.txt 文档中。第二个:从 1.txt 文档中,获取链接地址,再从相应的地址获取需要的论文标题和内容信息,保存到result.txt。

第一次完成的时候,截取的链接地址有两个不符合要求,改了很多次正则表达式,甚至把对的改错了!最后发现的问题是:有两个链接的地址中后面的 “CVPR”是小写,其他的是大写。发现这个的时候很开心,因为究竟了许久,最后在逐字对照正确爬取和错误爬取链接的时候发现。
(2)已经爬取了result.txt 后,还有一个问题是,会产生一个1.txt 文件。于是便需要优化,优化便是把上面两个程序都封装成包,最后在主程序中调用,既解决了产生多余 1.txt 的要求,也满足了测试要求。

正确爬取结果

1611251-20190315191648128-616250293.png

五、关键代码

1.从文件中读取字符流到缓冲区,判断缓冲区的每一行字符,切割字符串(去掉非字母非数字的符号),将切割出来的字符串保存到字符数组中,并记录有效行

2.将字符数组中的字符串转换为小写格式(方便之后判断和输出)。循环判断字数组符中的字符串前4个字符是否是小写字母。如果是,则保存到哈希表中(方便字典序排序),并增加单词数。如果不是,则舍去该字符串。

3.有哈希表中存储的字符串,可以方便的输出单词和词频。

从文件中得到字符串

public void getWord()    {        LinkedHashMap
list = sortMap(maxWordNum); try { FileOutputStream fos = new FileOutputStream(fileOutput); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter buff = new BufferedWriter(osw); String content = "characters: " + fByteCount + "\r\n"; content += "words: "+ getFWordCount() + "\r\n"; content += "lines: "+ fRowCount + "\r\n"; Iterator
iterator = list.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); content += "<" + key.replace("=", ">: ") + "\r\n"; //System.out.println(key.replace("=", ">: ")); } //System.out.print(content); buff.write(content); buff.flush(); fos.close(); } catch (FileNotFoundException e) { // TODO e.printStackTrace(); } catch (IOException e) { // TODO e.printStackTrace(); } }
public void setWord()    {        try {            String fContent = "";            FileInputStream fis = new FileInputStream(fileInput);            InputStreamReader isr = new InputStreamReader(fis);            BufferedReader br = new BufferedReader(isr);            fWordCount = fByteCount = fRowCount = 0;            while ((fContent = br.readLine()) != null) {                if(fContent.length() > 3)//                {                    fRowCount ++;                    if(fContent.charAt(0) == 'T')                    {                        fContent = fContent.substring(6, fContent.length()-1 );                        fByteCount += fContent.length();//                        setMap(fContent,true);                    }                    else if(fContent.charAt(0) == 'A')                    {                        fContent = fContent.substring(9, fContent.length()-1 );//remove(Abstract: )                         fByteCount += fContent.length();//                        setMap(fContent,false);                    }                }            }            fis.close();        } catch (FileNotFoundException e) {            System.out.print("");            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }

判断字符是否符合要求

public void setMap(String fContent,boolean isTitle)    {        String [] ch = fContent.split("\\W+");        for(int i = 0; i< ch.length ;i++)        {            if(ch[i].length()>=4)            {                ch[i] = ch[i].toLowerCase();                if (isLower(ch[i].charAt(0)) && isLower(ch[i].charAt(1)) && isLower(ch[i].charAt(2)) && isLower(ch[i].charAt(3)) )                {                    //System.out.print(ch[i]);                    fWordCount ++;                    if( map.containsKey(ch[i]) )                        map.put(ch[i],(wValue & isTitle) ? map.get(ch[i])+10 : map.get(ch[i])+1);                    else                         map.put(ch[i], (wValue & isTitle) ? 10 : 1);                }            }        }    }

哈希表排序(方便输出字典序)

public LinkedHashMap
sortMap(int num) { List
> list = new ArrayList
>(map.entrySet()); Collections.sort(list, new Comparator
>() { public int compare(Map.Entry
o1, Map.Entry
o2) { return o2.getValue() != o1.getValue() ? (o2.getValue() - o1.getValue()) : (o1.getKey()).toString().compareTo(o2.getKey()); //return (o1.getKey()).toString().compareTo(o2.getKey()); } }); LinkedHashMap
tmp = new LinkedHashMap
(); for (int i = 0; i < list.size() && i< num; i++) { String id = list.get(i).toString(); Integer value = list.get(i).getValue(); tmp.put(id, value); //System.out.println(id + (value)); } return tmp; }

六、关于队友

队友很强!我也会努力!

队友在代码实现和优化方面,很值得我学习。就比如说,在基础需求的字典排序中,我还在思考是不是用字符数组保存字符串后,比较相同字符串,然后进行字典排序。这样的方法,想想就很麻烦!而队友已经想到用哈希表保存数据,无论是字典安排序和输出,都很方便。

其次,这次作业在两人的沟通上远不如上一次,作业类型不同是一个原因。还有原因是,我得加强代码能力,希望能做到和队友基本同步。

七、总结

在一番辛勤劳动以及一份充硕的收获之后,感悟颇多!

当时在阅读理解同伴的基础需求代码上就有一些想法,现在补上。
大学已经三年了!过去的时光零零散散,孤独的,寂寞的,大笑的,啜泣的。无论以前如何,现在都需要将心境沉浸下来了!而如今,有一个优秀的同伴,我很幸运。就像同样学过数据结构,同伴能灵活运用,而我还是处于老旧的思想。我要学习这样的思维方法,这一次的作业,是一个好的开端,希望能继续下去。
心里的感悟颇多,但到了嘴边,好像一切都简单了!
简单便简单罢!再说一说自学的感受。当一座你恐惧的高山被你踩再脚下时,再看看四周,便有“会当凌绝顶,一览众山小”的豪情,心中无限舒畅。就算当初学习过程中,陷入困境时,也曾无奈。挣扎着,想要抓住什么当作救命稻草。会发现,能依靠的有朋友,有自己。
当匆忙时,会过得很充实。偶尔停下脚步,会发现,这一刻以前忽略了的闲暇,是那么的美好舒服。

八、发现问题

总结了收获,也有一些问题发现。

第一个是关于交流讨论和实践编码的
上一次作业中,交流讨论的时间很多,也从讨论中明确了工作的需求和目的。在实际工作的时候,可以引用一个成语就是:胸有成竹。
但这次作业中,一但涉及编写代码,讨论的时间就会减少很多。而且当同伴写出代码后,就不知道从哪里插手,好像自己修改就会破坏了同伴代码的完整性。
这里需要助教老师给我们解答一下疑惑。
第二个是关于github使用的
老实说,因为以前没有接触过,而且是全英文的。根据老师的作业要求,fork 了链接,新建文件夹以及Pull Request ,等等操作后,不知道是否正确。我当时是建立了一个文件,然后把完成的代码复制粘贴上去,选择了new Pull Request。但后面又经过同学的讲解,好像要下载一个github,然后克隆文件什么的。操作起来依旧是不知方向。
而我想向老师提议一下,如果下次有什么新的网站啊,还是软件什么需要用到。能不能像很多百度指导一样,给一些图片加上红色箭头标记,这样能让我们快速的熟悉运用这些网站。也许在熟练的使用者眼里,这只是简单的操作,但对于初学者来说,是莫大的帮助了!

转载于:https://www.cnblogs.com/73ECH/p/10539227.html

你可能感兴趣的文章
windows 下安装Simplejson方法
查看>>
IE并发连接限制(as)AS队列加载类(as3和as2)
查看>>
转:Android View.post(Runnable )
查看>>
ChinaTest第二天
查看>>
图灵等价和图灵完备
查看>>
CSS中position的absolute和relative的应用
查看>>
对 makefile 中 二次扩展的一点理解
查看>>
SET XACT_ABORT on
查看>>
记录mysql性能查询过程
查看>>
数据连接 DataDirectory 中的作用
查看>>
持续更新 iText in Action 2nd Edition中文版 个人翻译
查看>>
树、森林和二叉树的转换
查看>>
SSH重新登录的问题
查看>>
sys.path.insert(0, os.path.join('..', '..', '..', '..','...')) 解释
查看>>
开启mysql慢查询日志
查看>>
WEB项目的分层结构
查看>>
如果通过key获取dictionary里面的value
查看>>
【Java学习笔记】使用split()方法分割字符串
查看>>
我的架构经验系列文章 - 前端架构
查看>>
C#和C++的区别
查看>>