格式描述git
==========github
课程名称:软件工程1916|W(福州大学)正则表达式
做业要求:结对第二次—文献摘要热词统计及进阶需求算法
结对学号:221600408_蔡鸿键 | 221600409_蔡森林express
做业目标:编程
1、基本需求:实现一个可以对文本文件中的单词的词频进行统计的控制台程序。windows
2、进阶需求:在基本需求实现的基础上,编码实现顶会热词统计器。app
做业格式描述:该博客首段编辑器
做业完成工具: IDEA & githubide
博客编辑器: MARKDOWN
PSP:正文
github: WordCount进阶篇
做业目录
==========
1.WordCount基本需求
2.思路分析
3.关键代码
4.测试图片
5.困难与解决
6.心得与总结
7.psp与花絮
做业正文
==========
2.WordCount基本需求
(一)WordCount基本需求
实现一个命令行程序,不妨称之为wordCount。
统计文件的字符数:
只须要统计Ascii码,汉字不需考虑
空格,水平制表符,换行符,均算字符
统计文件的单词总数,单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。
英文字母: A-Z,a-z
字母数字符号:A-Z, a-z,0-9
分割符:空格,非字母数字符号
例:file123是一个单词,123file不是一个单词。file,File和FILE是同一个单词
统计文件的有效行数:任何包含非空白字符的行,都须要统计。
统计文件中各单词的出现次数,最终只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。
按照字典序输出到文件result.txt:例如,windows95,windows98和windows2000同时出现时,则先输出windows2000
输出的单词统一为小写格式
输出的格式为
characters: number
words: number
lines: number
...
(二)WordCount进阶需求
新增功能,并在命令行程序中支持下述命令行参数。说明:字符总数统计、单词总数统计、有效行统计要求与我的项目相同
从CVPR2018官网爬取今年的论文列表,输出到result.txt(必定叫这个名字),内容包含论文题目、摘要,格式以下:
为爬取的论文从0开始编号,编号单独一行
两篇论文间以2个空行分隔
在每行开头插入“Title: ”、“Abstract: ”(英文冒号,后有一个空格)说明接下来的内容是论文题目,或者论文摘要
后续全部字符、单词、有效行、词频统计中,论文编号及其紧跟着的换行符、分隔论文的两个换行符、“Title: ”、“Abstract: ”(英文冒号,后有一个空格)均不归入考虑范围
本部分不参与自动化测试,若有完成,需在博客中详细描述,并在博客中附件(.exe及.txt)为证。附加功能的加入不能影响上述基础功能的测试,分数取决于创意和所展现的完成度,创意没有天花板,这里不提出任何限制,尽大家所能去完成。
1 . 解题思路
整体:咱们先抓住需求中的统计函数的关键点,再思考分析一些边缘条件,给出规定,优化函数。
具体:1.WordCount基本需求:从文本文件中逐个读取全部字符,而后将其保存为字符串,接着经过正则表达式匹配单词,保存于TreeMap中,统计并排序,最后将统计结果输出到文件中。
2.WordCount进阶需求:经过Jsoup工具将CVPR2018官网的论文列表爬取下来,保存在result文本文件中,一样也是进行逐个字符读取统计,保存字符串进行相应的统计和排序,最后输出结果。
2 . 设计过程
WordCount的基本需求
- 类图
- 活动图
WordCount的进阶需求
- 类图
- 活动图
3 . 关键代码
WordCount基本需求
//统计字符数 public static void countChars()throws Exception{ countChars = 0; File file = new File(path); BufferedReader br = new BufferedReader(new FileReader(file)); StringBuffer sbf = new StringBuffer(); int val; while((val=br.read())!=-1){ if((0<=val && val<=9)){ countChars++; }else if(val == 10){ continue; }else if(11<=val && val<=127){ countChars++; } sbf.append((char)val); } br.close(); fileContent = sbf.toString().toLowerCase(); }
//统计有效行数 public static void countLines()throws Exception{ countLines = 0; String regex = "\\s*"; File file = new File(path); BufferedReader br = new BufferedReader(new FileReader(file)); String line; while((line=br.readLine())!=null){ if(!line.matches(regex)){ countLines++; } } br.close(); }
//统计单词数 public static void countWords(){ countWords = 0; Pattern expression = Pattern.compile("[a-z]{4,}[a-z0-9]*"); String str = fileContent; Matcher matcher = expression.matcher(str); String word; while (matcher.find()) { word = matcher.group(); countWords++; if (records.containsKey(word)) { records.put(word, records.get(word) + 1); } else { records.put(word, 1); } } }
//统计热门单词个数 public static void countHotWords(){ sortWord(); String str; int length = wordList.size(); if(length > 10){ for(int i = 0; i < 10; i++){ str = wordList.get(i); hotWordList.add(str); } } else{ hotWordList.addAll(wordList); } }
//对统计完的单词进行排序 public static void sortWord(){ List<Map.Entry<String, Integer>> list = new ArrayList<>(records.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); } }); String str; for (Map.Entry<String, Integer> e: list) { str = "<"+e.getKey()+">: "+e.getValue(); wordList.add(str); } }
WordCount进阶需求
//线程爬取官网连接 public static void crawler(){ try{ String url = "http://openaccess.thecvf.com/CVPR2018.py"; crawlerLink(url); new Thread(new Runnable() { @Override public void run() { while(!links.isEmpty()){ String link = links.getFirst(); links.removeFirst(); crawlerContent(link); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); }catch (Exception e){ e.printStackTrace(); } }
//爬取全部文章连接 public static void crawlerLink(String url){ try{ Document doc = Jsoup.connect(url).get(); Element content = doc.getElementById("content"); Elements ptitles = content.getElementsByClass("ptitle"); for(Element item : ptitles){ Element link = item.selectFirst("a"); String href = "http://openaccess.thecvf.com/"+link.attr("href"); links.add(href); } }catch (Exception e){ e.printStackTrace(); } }
//获取全部文章的标题和摘要 public static void crawlerContent(String url){ try{ StringBuilder results = new StringBuilder(); Document doc = Jsoup.connect(url).get(); Element content = doc.getElementById("content"); results.append(index+"\r\n"); String title = content.getElementById("papertitle").text(); results.append("title: "+title+"\r\n"); String abstractText = content.getElementById("abstract").text(); results.append("abstract: "+abstractText+"\r\n"+"\r\n"+"\r\n"); saveArticle(results); index++; }catch (Exception e){ e.printStackTrace(); } }
//将爬取的内容保存到result文本 public static void saveArticle(StringBuilder content)throws Exception{ File file = new File(path+"result.txt"); if(!file.exists()){ file.createNewFile(); } FileWriter fw = new FileWriter(file,true); BufferedWriter bw = new BufferedWriter(fw); bw.write(content.toString()); bw.flush(); bw.close(); }
if(args.length == 0){ throw new IllegalArgumentException(); } for (int i = 0; i < args.length; i++) { if(args[i].equals("-i")){ orders.put("-i",args[i+1]); i++; }else if(args[i].equals("-o")){ orders.put("-o",args[i+1]); i++; }else if(args[i].equals("-w")){ orders.put("-w",args[i+1]); i++; }else if(args[i].equals("-m")){ orders.put("-m",args[i+1]); i++; }else if(args[i].equals("-n")){ orders.put("-n",args[i+1]); i++; } }
//命令行参数赋值 public static void terminal(){ inputPath = path + orders.get("-i"); outputPath = path + orders.get("-o"); w = Integer.valueOf(orders.get("-w")); if(orders.containsKey("-m")){ m = Integer.valueOf(orders.get("-m")); isGroup = true; } if(orders.containsKey("-n")){ n = Integer.valueOf(orders.get("-n")); }else{ n = 10; } }
//读取输入文件全部字符内容 public static void readFile()throws Exception{ File file = new File(inputPath); BufferedReader br = new BufferedReader(new FileReader(file)); int val; while((val=br.read())!=-1){//统计全部字符数 if((0<=val && val<=9)){ countAllChars++; }else if(val == 10){ continue; }else if(11<=val && val<=127){ countAllChars++; } } br.close(); } //获取文章标题,摘要字符内容及有效行数 public static void dealFile()throws Exception{ String regex = "\\d+"; File file = new File(inputPath); if(!file.exists()){ file.createNewFile(); } BufferedReader br = new BufferedReader(new FileReader(file)); String line,title,abstractText; while((line=br.readLine())!=null){ if(line.matches(regex)){ countNums += line.length() + 1;//统计论文编号所在行字符数 } else if(line.startsWith("title: ")){ countLines++;//统计有效行数 countNums += 7;//统计title: 字符数 title = line.substring(6).toLowerCase(); if(w == 0){ countWords(title,1); }else if(w == 1){ countWords(title,10); } if(isGroup){ if(w == 0){ countWordGroups(title,1); }else if(w == 1){ countWordGroups(title,10); } } }else if(line.startsWith("abstract: ")){ countLines++;//统计有效行数 countNums += 10;//统计abstract: 字符数 abstractText = line.substring(9).toLowerCase(); countWords(abstractText,1); if(isGroup){ countWordGroups(abstractText,1); } } } br.close(); countHotWords(); if(isGroup){ countHotWordGroups(); } }
//统计单词 public static void countWords(String content, int weight){//weight权重的词频统计 Pattern expression = Pattern.compile("[a-z]{4,}[a-z0-9]*");//匹配单词 String str = content; Matcher matcher = expression.matcher(str); String word; while (matcher.find()) { word = matcher.group(); countWords++; if (records.containsKey(word)) { records.put(word, records.get(word) + weight); } else { records.put(word, weight); } } }
//统计单词词组 public static void countWordGroups(String content, int weight){//词组权重词频统计 StringBuffer sbf = new StringBuffer(); int size = content.length(); char []arr = content.toCharArray(); for (int i = 0; i < size; i++) {//去除分隔符整合全部字符 char val = arr[i]; if(48<=val && val<=57){ sbf.append(val); }else if(97<=val && val<=122){ sbf.append(val); } } allText = sbf.toString();//获取全部字符(去分隔符后) Pattern expression = Pattern.compile("[a-z]{4,}[a-z0-9]*"); String str = content; Matcher matcher = expression.matcher(str); String word; ArrayList<String> group = new ArrayList<>(); while (matcher.find()) {//提取单词 word = matcher.group(); group.add(word);//单词归组 } int len = group.size(); for (int i = 0; i <= len-m; i++) { String pr = ""; String pr2 = ""; for (int j = i; j < i+m; j++) {//将m个单词构成字符串pr,pr2为记录单词构成的词组 pr += group.get(j); pr2 += group.get(j); if(j < (i+m)-1){ pr2 +=" "; } } if(allText.indexOf(pr)!=-1){//在allText中匹配子字符串pr if (records2.containsKey(pr2)) {//词组归组 records2.put(pr2, records2.get(pr2) + weight); } else { records2.put(pr2, weight); } } } }
//统计热度单词 public static void countHotWords(){ sortWords(); String str; int length = wordList.size(); if(length > n){ for(int i = 0; i < n; i++){ str = wordList.get(i); hotWordList.add(str); } } else{ hotWordList.addAll(wordList); } }
//统计热度词组 public static void countHotWordGroups(){ sortWordGroups(); String str; int length = wordGroupList.size(); if(length > n){ for(int i = 0; i < n; i++){ str = wordGroupList.get(i); hotWordGroupList.add(str); } } else{ hotWordGroupList.addAll(wordGroupList); } }
//单词排序 public static void sortWords(){ List<Map.Entry<String, Integer>> list = new ArrayList<>(records.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); } }); String str; for (Map.Entry<String, Integer> e: list) { str = e.getKey()+":"+e.getValue(); wordList.add(str); } }
//词组排序 public static void sortWordGroups(){ List<Map.Entry<String, Integer>> list = new ArrayList<>(records2.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); } }); String str; for (Map.Entry<String, Integer> e: list) { str = "<"+e.getKey()+">:"+e.getValue(); wordGroupList.add(str); } }
4.WordCount基本需求单元测试
- 样例1
- 测试1结果
- 样例2
- 测试2结果
- 样例3
- 测试3结果
5.交流与讨论
D:每一个人分工不均
A:多商量多讨论
D:需求理解不到位
A:分析理解讨论需求
D:代码BUG改不出来
A:多多时候搜索引擎
6.困难与解决
7.心得与总结
最大的收获,应该就是发现问题,解决问题的过程,有时候由于一小小的细节,弄得本身焦头烂额,可是当忽然找到问题的所在时,内心顿时轻松了不少。这或许是调试bug必然的过程。此外,咱们也学到了一些实用的技能,如Jsoup工具的使用,GitHub的项目的上传等。
此次结对我对于团队的做用比较小,主要负责文档的编写与GITHUB的上传和代码测试优化,在代码这一块涉及比较小,主要是由于分工不明。我认为之后团队编码时应该你们先聚在一块儿讨论需求题目,相互信任,这才会使团队做用最大化。
队友此次主要负责代码部分,特别认真,考虑周到,分析需求到位。
此次结对编程,整体来讲,收获仍是挺大的,我主要负责代码编程,不少时候常常被一小bug弄得焦头烂额,可是有时候忽然灵光一现,那又是多么使人开心的事。这次的编程,不只让个人编程能力获得了锻炼,也让我学到了一些新的知识,更重要的是,团队协做,不只能够督促本身,并且更能提升效率。
队友此次主要负责项目上传,博客撰写以及后期代码测试和优化,时不时给我提供思路参考。
此次任务涉及代码部分但实际上是模仿如今公司的代码编程规范,如今让咱们接触这些规范是有好处的,能及时的改为咱们的错误的编程,培养良好的,高效率的编程。这是很是重要的。
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分 钟) |
Planning | 计划 | ||
• Estimate | • 估计这个任务须要多少时间 | 600 | 550 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 30 | 60 |
• Design Spec | • ⽣成设计⽂档 | 20 | 30 |
• Design Review | • 设计复审 | 20 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
• Design | • 具体设计 | 120 | 250 |
• Coding | • 具体编码 | 360* | 480* |
• Code Review | • 代码复审 | 30 | 30 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 20 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 10 | 15 |
• Size Measurement | • 计算工做量 | 15 | 10 |
• Postmortem & Process Improvement Plan | • 过后总结, 并提出过程改进计划 | 30 | 25 |
合计 | 685 | 950 |