结对第二次—文献摘要热词统计及进阶需求

课程链接:软件工程1916|W(福州大学)
作业要求:结对第二次—文献摘要热词统计及进阶需求
结对学号:221600205 | 221600207
作业目标1:一、基本需求:实现一个能够对文本文件中的单词的词频进行统计的控制台程序。
作业目标2:二、进阶需求:在基本需求实现的基础上,编码实现顶会热词统计器

团队分工:

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

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

作业正文

一、PSP表格

PSP2.1Personal 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
合计11102210

二、解题思路描述

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

记得开始工作的时候是上周六(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<String,Integer> 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<String> 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<String,Integer> sortMap(int num)
    {
        List<Map.Entry<String,Integer>> list = new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {   
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {      
                return o2.getValue() != o1.getValue() ? (o2.getValue() - o1.getValue()) : (o1.getKey()).toString().compareTo(o2.getKey());
                //return (o1.getKey()).toString().compareTo(o2.getKey());
            }
        });
        LinkedHashMap<String,Integer> tmp = new LinkedHashMap<String,Integer>();
        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(题目),题目被包含在<div id="papertitle"> 和 </div>中
                Pattern p1 = Pattern.compile("(<div id=\"papertitle\">)(.+?)(</div>*)");            
                Matcher m1 = p1.matcher(html);  
                // 匹配Abstract(摘要),摘要被包含在<div id="abstract"> 和 </div>中
                Pattern p2 = Pattern.compile("(\"abstract\")(.+?)(</div>*)");
                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("(<div id=\"content\">)(.*)(</div>*)");         
        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<String,Integer> 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<String> 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<String,Integer> sortMap(int num)
    {
        List<Map.Entry<String,Integer>> list = new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {   
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {      
                return o2.getValue() != o1.getValue() ? (o2.getValue() - o1.getValue()) : (o1.getKey()).toString().compareTo(o2.getKey());
                //return (o1.getKey()).toString().compareTo(o2.getKey());
            }
        });
        LinkedHashMap<String,Integer> tmp = new LinkedHashMap<String,Integer>();
        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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值