結對第二次—文獻摘要熱詞統計及進階需求

格式描述git

==========github

1、基本需求:實現一個可以對文本文件中的單詞的詞頻進行統計的控制檯程序。windows

2、進階需求:在基本需求實現的基礎上,編碼實現頂會熱詞統計器。app


  • 做業格式描述:該博客首段編輯器

  • 做業完成工具: IDEA & githubide

  • 博客編輯器: MARKDOWN

  • PSP:正文

  • github:
  • JAVA1
  • JAVA2


做業目錄

==========

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

: number

: number

...

(二)WordCount進階需求

新增功能,並在命令行程序中支持下述命令行參數。說明:字符總數統計、單詞總數統計、有效行統計要求與我的項目相同

  1. 使用工具爬取論文信息

從CVPR2018官網爬取今年的論文列表,輸出到result.txt(必定叫這個名字),內容包含論文題目、摘要,格式以下:

爲爬取的論文從0開始編號,編號單獨一行

兩篇論文間以2個空行分隔

在每行開頭插入「Title: 」、「Abstract: 」(英文冒號,後有一個空格)說明接下來的內容是論文題目,或者論文摘要

後續全部字符、單詞、有效行、詞頻統計中,論文編號及其緊跟着的換行符、分隔論文的兩個換行符、「Title: 」、「Abstract: 」(英文冒號,後有一個空格)均不歸入考慮範圍

  1. 附加題(20')

本部分不參與自動化測試,若有完成,需在博客中詳細描述,並在博客中附件(.exe及.txt)爲證。附加功能的加入不能影響上述基礎功能的測試,分數取決於創意和所展現的完成度,創意沒有天花板,這裏不提出任何限制,盡大家所能去完成。

1 . 解題思路

整體:咱們先抓住需求中的統計函數的關鍵點,再思考分析一些邊緣條件,給出規定,優化函數。
具體:1.WordCount基本需求:從文本文件中逐個讀取全部字符,而後將其保存爲字符串,接着經過正則表達式匹配單詞,保存於TreeMap中,統計並排序,最後將統計結果輸出到文件中。
2.WordCount進階需求:經過Jsoup工具將CVPR2018官網的論文列表爬取下來,保存在result文本文件中,一樣也是進行逐個字符讀取統計,保存字符串進行相應的統計和排序,最後輸出結果。

2 . 設計過程

  • 代碼結構
    • WordCount的基本需求
      - 類圖

      - 活動圖

    • WordCount的進階需求
      - 類圖

      - 活動圖

  • 階段一
    • 共同討論WordCount的基本功能需求
    • 代碼基本實現WordCount的基本需求
    • 將項目各個功能分離成獨立函數並進行相應的測試調整
    • 對代碼進行相應的測試和調整優化
  • 階段二
    • 共同討論WordCount的進階功能需求
    • 實現CVPR2018官網的論文爬取整理
    • 代碼基本實現WordCount的進階需求
    • 對代碼進行相應的測試和調整優化
  • 階段三
    • 將項目按照做業格式上傳github
    • 撰寫博客

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進階需求

    • 線程爬取CVPR2018官網
    //線程爬取官網連接
      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();
          }
      }
    • 爬取CVPR2018官網全部文章連接
    //爬取全部文章連接
      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();
          }
      }
    • 獲取CVPR2018全部文章的標題和摘要
    //獲取全部文章的標題和摘要
      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();
          }
      }
    • 將CVPR2018官網爬取的內容保存到result文本
    //將爬取的內容保存到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;
          }
      }
    • 統計字符數和有效行數(countChars = countAllChars - countNums)
    //讀取輸入文件全部字符內容
      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.困難與解決

  • 對於統計文件的字符數,本來打算經過readLine()方法讀取求取長度以獲取字符數,可是忽然發現行不通,由於readLine()不能讀取換行,並且不能判斷最後一行是否換行,以及空白行,最後決定採用read()方法逐個字符讀取。
  • 對於統計文件的有效行數,原本想借用read()讀取到的「\n」字符來判斷行數,忽然發現空白行不能解決,照樣統計,因而改用readLine()方法,藉助正則表達式匹配空行的方法解決。
  • 對於單詞個數的統計,正則表達式一步到位。可是對於TreeMap的排序,本身還不很懂,最後經過百度,找到Collections接口調用排序得以解決。
  • 對於「Core模塊」的功能,我還不是很懂,不知道怎麼把統計字符數,統計單詞數,統計最多的10個單詞及其詞頻這三個功能獨立出來,總感受三個功能模塊聯繫密切,最後也只是簡單地將每一個功能單獨成函數形式簡單分離。
  • 對於Jsoup爬蟲工具的使用還不是很熟練,如元素的定位和信息的提取常常出錯。
  • 對於統計論文當中的字符數,剛開始用readLine()方法讀取title和abstract內容,發現並不能正確統計字符數,當中存在棘手的問題,就是換行符的統計不許確,最後決定採用總字符數減掉論文編號所在行的字符數,title:和abstract:的字符數。
  • 對於單詞詞組的統計,剛開始無從下手,想採用正則表達式匹配,忽然發現該怎麼構建正則表達式,最後想了好久,決定採用擬字符串匹配算法的方法。也就是說,先把多個單詞構成字符串,接着把要出來的字符串進行去分隔符的整合處理成大字符串,而後用子字符串去匹配,如匹配成功,則該詞組知足要求,反之不成立,最後將知足要求的詞組進行詞頻統計並保存。

7.心得與總結
最大的收穫,應該就是發現問題,解決問題的過程,有時候由於一小小的細節,弄得本身焦頭爛額,可是當忽然找到問題的所在時,內心頓時輕鬆了不少。這或許是調試bug必然的過程。此外,咱們也學到了一些實用的技能,如Jsoup工具的使用,GitHub的項目的上傳等。

蔡鴻鍵

此次結對我對於團隊的做用比較小,主要負責文檔的編寫與GITHUB的上傳和代碼測試優化,在代碼這一塊涉及比較小,主要是由於分工不明。我認爲之後團隊編碼時應該你們先聚在一塊兒討論需求題目,相互信任,這纔會使團隊做用最大化。

隊友此次主要負責代碼部分,特別認真,考慮周到,分析需求到位。

蔡森林

此次結對編程,整體來講,收穫仍是挺大的,我主要負責代碼編程,不少時候常常被一小bug弄得焦頭爛額,可是有時候忽然靈光一現,那又是多麼使人開心的事。這次的編程,不只讓個人編程能力獲得了鍛鍊,也讓我學到了一些新的知識,更重要的是,團隊協做,不只能夠督促本身,並且更能提升效率。

隊友此次主要負責項目上傳,博客撰寫以及後期代碼測試和優化,時不時給我提供思路參考。

總結

此次任務涉及代碼部分但實際上是模仿如今公司的代碼編程規範,如今讓咱們接觸這些規範是有好處的,能及時的改爲咱們的錯誤的編程,培養良好的,高效率的編程。這是很是重要的。

PSP

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
相關文章
相關標籤/搜索