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


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

課程鏈接 軟件工程1916|W(福州大學)
作業要求 結對第二次—文獻摘要熱詞統計及進階需求
結對學號 221600207|221600205
作業目標 本次作業分為兩部分:
一、基本需求:實現一個能夠對文本文件中的單詞的詞頻進行統計的控制台程序。
二、進階需求:在基本需求實現的基礎上,編碼實現頂會熱詞統計器
作業正文 作業正文
基礎需求 WordCount
進階需求 WordCountPro

提交截圖如下

一、寫在前面

本此作業中進階需求里的-m功能尚未完成,基本思路為將讀取的String分割為單詞,創建數組並依次記錄單詞出現的下標,非法單詞記錄下標為-1。即String="key,hello word"對應數組為a[0]=0;a[1]=-1;a[2]=4;a[3]=10;取m=2時則取出subString(a[2],a[3]+a[3].length);

二、團隊分工

221600207黃權煥

  • 完成基礎需求src目錄下的全部代碼
  • 完成進階需求src目錄下的全部代碼
  • 編寫博客
  • 軟件測試

221600205陳紅寶

  • 完成進階需求cvpr目錄下全部代碼
  • 編寫博客
  • 軟件測試

三、PSP 表格:

PSP2.1 Personal Software Process Stages 預估耗時(分鍾) 實際耗時(分鍾)
Planning 計划
• Estimate • 估計這個任務需要多少時間 900
Development 開發
• Analysis • 需求分析 (包括學習新技術) 120 600
• Design Spec • 生成設計文檔 60 60
• Design Review • 設計復審 30 60
• Coding Standard • 代碼規范 (為目前的開發制定合適的規范) 30 30
• Design • 具體設計 120 200
• Coding • 具體編碼 600 900
• Code Review • 代碼復審
• Test • 測試(自我測試,修改代碼,提交修改) 30 120
Reporting 報告 60 150
• Test Repor • 測試報告 30 30
• Size Measurement • 計算工作量 30 30
•Postmortem & Process Improvement Plan • 事后總結, 並提出過程改進計划 30 30
    |合計   | 1110  |2210  |

四、解題思路

1.基礎需求

獲取行數
將文件打開后,用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可以按插入順序保存,方便后續使用。時間復雜度N*log(N);

2.進階需求

自定義輸入輸出
在類中額外保存輸入輸出名即可。

自定義詞頻統計輸出
在類中額外保存一個最大單詞數用來控制LinkedHashMap長度即可。

權重分析
在HashMap插值時,額外判斷是否來自Title,是的話記錄數+10,否則+1即可。

詞組詞頻統計功能
詞組詞頻統計分析由於時間關系尚未實現,思路在寫在前面已介紹過。將讀取的String分割為單詞,創建數組並依次記錄單詞出現的下標,非法單詞記錄下標為-1。即String="key,hello word"對應數組為a[0]=0;a[1]=-1;a[2]=4;a[3]=10;取m=2時則取出subString(a[2],a[3]+a[3].length);取m=1時則取出subString(a[2],a[2]+a[2].length)等。

多參數的混合使用
讀取一行,依舊用split("-")函數分割成不同指令,分別調用函數即可。

3.爬蟲(由我隊友00205完成)

諸位還是到我隊友文章查看,由於本人無法總結,且他文章字數太多,我不再贅述。221600205

五、代碼組織與測試

lin.java完成函數的封裝。其中setWord接受文件名,調用setMap,調用isLow;getWord輸出文件,調用sortMap。

Main.java進行調用和設置lib類的參數。

類圖如下

存放時的流程圖如下

單元測試圖

單元測試代碼

import static org.junit.Assert.*;

import org.junit.Test;

public class libTest {
	
	@Test
	public void testSetFileInput() {
	    lib b = new lib();
		b.setFileInput("input.txt");
		assertEquals("input.txt",b.getFileInput());
	}

	@Test
	public void testSetFileOutput() {
	    lib b = new lib();
		b.setFileOutput("output.txt");
		assertEquals("output.txt",b.getFileOutput());
	}

	@Test
	public void testSetWValue() {
	    lib b = new lib();
		b.setWValue(3);
		assertEquals(true,b.getWValue());
	}

	@Test
	public void testSetMValue() {
	    lib b = new lib();
		b.setMValue(5);
		assertEquals(5,b.getMValue());
	}

	@Test
	public void testSetMaxWordNum() {
	    lib b = new lib();
		b.setMaxWordNum(5);
		assertEquals(5,b.getMaxWordNum());
	}

	@Test
	public void testGetFWordCount() {
	    lib b = new lib("input.txt");
	    b.setWord();
		assertEquals(9,b.getFWordCount());
	}

	@Test
	public void testGetFRowCount() {
	    lib b = new lib("input.txt");
	    b.setWord();
	    assertEquals(2,b.getFRowCount());
	}

	@Test
	public void testGetfByteCount() {
	    lib b = new lib("input.txt");
        b.setWord();
        b.getWord();
        assertEquals(74,b.getfByteCount());
	}
	
	@Test
	public void testGetMaxWordNum() {
	    lib b = new lib("input.txt");
        b.setMaxWordNum(12);
        assertEquals(12,b.getMaxWordNum());
	}

	@Test
	public void testIsLower() {
	    lib b = new lib();
	    assertEquals(false,b.isLower('A'));
	}

	@Test
	public void testIsDigit() {
	    lib b = new lib();
        assertEquals(true,b.isDigit('0'));
	}
	
}

六、關鍵代碼實現

1.類的數據字段

public class lib {
	private int fWordCount = 0;//字詞數
	private int fRowCount = 0;//行數
	private int fByteCount = 0;//字節數
	private int maxWordNum = 10;//詞頻輸出數
	private String fileInput= "cvpr/result.txt";
	private String fileOutput= "src/output.txt";
	private boolean wValue = false;//-w 值
	private int mValue = 1;//-m 值
	private HashMap<String,Integer> map = new HashMap<String,Integer>();//存放字詞處
	//……
}

2.讀入文檔並統計

    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(7, fContent.length()-1 );//remove(Title:) 
                        //換行+1
                        fByteCount += fContent.length()+1;
                        if(mValue >= 1 )
                            setMap(fContent,true); 
                        else 
                            setMapPro(fContent,true);
                    }
                    else if(fContent.charAt(0) == 'A')
                    {
                        fContent = fContent.substring(9, fContent.length()-1 );//remove(Abstract: ) 
                        //換行+1
                        fByteCount += fContent.length()+1;
                        if(mValue >= 1 )
                            setMap(fContent,false); 
                        else 
                            setMapPro(fContent,false);
                    }
                }
            }
            fis.close();
        } catch (FileNotFoundException e) {
            System.out.print("文件不存在");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3、根據字符串分割成單詞並保存

    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]);
                    //新增紀錄或者記錄數+1
                    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);
                }
            }
        }
    }

4.Map排序

    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;
    }

5.文檔輸出(可自定義)

    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 自動生成的 catch 塊
            e.printStackTrace();
        } catch (IOException e) {
            // TODO 自動生成的 catch 塊
            e.printStackTrace();
        }
    }

七、算法改進

在sortMap排序中,我使用的是java自帶的sort函數,故時間效率為N*log(N).但是我們也可以改為維護一個最大堆MaxHeap,設定堆的容量為maxWordNum,以下簡稱M。則時間效率可以改進為N*log(M),在通常情況下,M很小,log(M)約等於1,可惜java沒有現成的最大堆。

八、總結與隊友評價

本次任務時間相對緊張,像性能測試和單元測試並沒有做得很好。這次結對暴露出一些問題,我是想好的時間計划很容易被隊友的編程速度打敗。比如這次,我獨立完成了基礎需求和進階需求的大部分代碼,即便如此,我依舊是在作業發布的第五天才收到我隊友的爬蟲相關代碼。

另外不可否認的是,項目進度拖延也有我的一部分責任,盡管我是近乎每天都有跟進與催促,但效果甚微,我卻在完善和測試自己的代碼,沒有過多給與支持(可能我是不想一個人完成整個項目吧,攤手.jpg)

對於這次項目的要求,GitHub、單元測試與jProfiler的使用也花費了我不少時間,且由於最初測試時沒有使用單元測試,函數也不夠細致,導致后續的單元測試只能非常簡單,性能分析也草草結束。(這三項幾乎花了我一半的代碼時間,不能小覷)。

關於隊友,我還是認可他的學習態度的,他也因熬夜寫代碼而熬到了四點余。盡管我接觸的爬蟲是依據文檔樹查找葉節點實現的,而他是通過匹配正則而實現,但我並不清楚他這么做到底有多難,也就無從評價,還是請諸君移步至他的博客一觀。

在文章末尾,我也想借此提出疑問:一個軟件項目中,你無法准確評估隊友的編程能力高低,那么是事前分工和計划被打破的情況下,如何重新組織分工與計划呢?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM