軟件工程 做業二

軟件工程 做業二


1.碼雲項目地址

https://gitee.com/cxf1404445126/PersonalProject-Java/tree/mastergit


2.PSP表格

PSP2.1 我的開發流程 預估耗費時間(分鐘) 實際耗費時間(分鐘)
Planning 計劃 30 40
· Estimate 明確需求和其餘相關因素,估計每一個階段的時間成本 30 40
Development 開發 600 680
· Analysis 需求分析 (包括學習新技術) 60 100
· Design Spec 生成設計文檔 60 50
· Design Review 設計複審 30 15
· Coding Standard 代碼規範 30 15
· Design 具體設計 100 80
· Coding 具體編碼 180 180
· Code Review 代碼複審 30 20
· Test 測試(自我測試,修改代碼,提交修改) 70 180
Reporting 報告 80 90
· 測試報告 30 30
· 計算工做量 20 20
· 並提出過程改進計劃 30 40

3.解題思路描述

  1. 首先固然先選擇好語言,嗯…Java無疑了。
  2. 下一步是理清題目中須要用到編程方面的知識點。
    • 很明顯要求的重點是經過文本文件對其進行統計,那就是要考慮Java中關於文件的應用了;
    • 其次是對讀取字符進行統計,包括大小寫、單詞等要求,那想來要用到到字符串匹配判斷方面的知識點;
    • 接着統計好的單詞要進行分類存放,使用什麼呢,Map?List?Set?嗯都先記下來好了,等到要使用的到了再根據具體狀況決定。
  3. 大概總結完知識點,而後是開始進行類的劃分,肯定一下開始的方向。
  4. 最後就是根據劃分出的組織順序開始代碼的編寫,至於資料,通常是等到須要用到的時候直接在網上進行查找,我的感受這樣目的性比較明確,效率會高一些。

4.設計實現過程

1、相關類設計

  • WordDeal類,用於存放統計字符數、行數、單詞數、詞頻等方法。
  • FileDeal類做爲文件處理類,用於讀取文件內容以及寫入文件。
  • Main類,調用上面兩個類的方法,實現具體功能。
  • Test類,用於進行單元測試。

2、相關函數設計

  1. FIleDeal類中的方法編程

    • FileToString方法實現讀取一個文件,而且返回文件內容,保存在一個字符串中。
    • WriteToFile方法實現把統計結果寫入指定文件。
  2. WordDeal類中的方法數組

    • getCharCount()方法實現統計字符數。
    • getLineCount()方法實現計算行數。
    • getWordCount()方法實現計算單詞數。
    • getWordFreq()方法實現統計每個單詞的詞頻。
    • sortMap()方法實現對Map中的數據進行排序,先按詞頻數後按字典順序。
    • ListToArray()方法實現取出詞頻數前十的數據。
  3. 關鍵函數流程圖ide




5.代碼說明

1. getCharCount()函數

public int getCharCount() // 統計文件字符數(ascll碼(32~126),製表符,換行符,)
 {
     char c;
     for (int i = 0; i < text.length(); i++) {
         c = text.charAt(i);
         if (c >= 32 && c <= 126 || c == '\r' || c == '\n'|| c == '\t') {
             charNum++;
         }
     }
     return charNum;
 }

這個函數是用於統計字符的數量,主要實現方法就是將逐個字符進行遍歷,判斷它是否在有效字符內,若爲有效字符則進行記錄。函數

2. getWordCount()函數

public int getWordCount() // 統計單詞總數(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。)
 {
     String t = text;
     t = t.replace('\n', ' ');
     t = t.replace('\r', ' ');
     t = t.replace('\t', ' ');
     String[] spWord = t.split(" +"); // 對字符串進行分詞操做
     for (int i = 0; i < spWord.length; i++) {
         if (spWord[i].length() < 4) { // 判斷長度是否大於等於4
             continue;
         } else {
             int flag = 1; // 判斷字符串的前四位是不是英文字母
             char[] ch = spWord[i].toCharArray();
             for (int j = 0; j < 4; j++) {
                 if (!(ch[j] >= 'A' && ch[j] <= 'Z' || ch[j] >= 'a' && ch[j] <= 'z')) {
                     flag = 0;
                 }
             }
             if (flag == 1) {
                 wordCount++;

             }
         }
     }
     return wordCount;
 }

這個函數是用於判斷單詞的個數,由於文件中存在換行符製表符等分割符,因此方便分割字符,先統一將它們都替換成了空格,而後以空格爲標誌進行分割,接着按照單詞的要求進行單詞的判斷便可。性能

3. getLineCount()函數

public int getLineCount() { // 統計有效行數

     String[] line = text.split("\r\n"); // 將每一行分開放入一個字符串數組
     for (int i = 0; i < line.length; i++) { // 找出無效行,統計有效行

         if (line[i].trim().length() == 0)
             continue;
         ValidLine = ValidLine + 1;
     }
     return ValidLine;
 }

該函數用於統計有效行數,只須要將文件的每一行進行分割,存入字符數組,而後判斷有無空行,即對應的字符去掉空格後長度是否爲0,而後記錄下有效行數並返回。單元測試

4. getWordFreq()函數

public Map getWordFreq() // 統計單詞詞頻(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。)
 {
     wordFreq = new HashMap<String, Integer>();
     String t = text;
     t = t.replace('\n', ' ');
     t = t.replace('\r', ' ');
     t = t.replace('\t', ' ');
     String[] spWord = t.split(" +"); // 對字符串進行分詞操做
     for (int i = 0; i < spWord.length; i++) {
         if (spWord[i].length() < 4) { // 判斷長度是否大於等於4
             continue;
         } else {
             // System.out.println(spWord[i]+" "+spWord[i].length());
             int flag = 1; // 判斷字符串的前四位是不是英文字母
             char[] ch = spWord[i].toCharArray();
             for (int j = 0; j < 4; j++) {
                 if (!(ch[j] >= 'A' && ch[j] <= 'Z' || ch[j] >= 'a' && ch[j] <= 'z')) {
                     flag = 0;
                 }
             }
             if (flag == 1) { // 將字符串轉化爲小寫
                 spWord[i] = spWord[i].trim().toLowerCase();
                 if (wordFreq.get(spWord[i]) == null) { // 判斷以前Map中是否出現過該字符串
                     wordFreq.put(spWord[i], 1);
                 } else
                     wordFreq.put(spWord[i], wordFreq.get(spWord[i]) + 1);

             }
         }
     }
     return wordFreq;
 }

該函數用於單詞詞頻的統計,以前的部分先按照單詞的判斷方法進行判斷一個詞是不是單詞,而後使用一個Map進行存儲,具體方法爲先判斷Map中以前是否存過該數據,若沒有存過,則將其存入並置value值爲1,若存過,則將該值的value值加1。學習

5. sortMap()函數

public List sortMap(Map wordCount) { // 對單詞詞頻的Map進行排序
     List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(wordCount.entrySet());
     Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {

         @Override
         public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {  //對Map中內容進行排序,先按詞頻後按字典順序
             if (o1.getValue() == o2.getValue()) {
                 return o1.getKey().compareTo(o2.getKey());
             }
             return o2.getValue() - o1.getValue();
         }

     });
     return list;
 }

這個函數用於對Map詞頻進行排序,這裏使用了Map類型的List來存放詞頻信息,而後經過重寫Comparator以及排序函數來實現相應要求的排序,最後返回排完序的List。測試


6.單元測試

此次的單元測試使用了Junit4,由於數量以及類似關係,僅展現WordDeal類中的一個函數的相關測試。編碼

  • 這裏採用的是白盒測試法進行測試,白盒法考慮的是測試用例對程序內部邏輯的覆蓋程度,要求是要儘量的使其覆蓋程度更大一些。

  • 對於白盒法的覆蓋標準有以下幾個:

    語句覆蓋:是一個比較弱的測試標準,它的含義是:選擇足夠的測試用例,使得程序中每一個語句至少都能被執行一次。它是最弱的邏輯覆蓋,效果有限,必須與其它方法交互使用。

    斷定覆蓋(也稱爲分支覆蓋):執行足夠的測試用例,使得程序中的每個分支至少都經過一次。斷定覆蓋只比語句覆蓋稍強一些,但實際效果代表,只是斷定覆蓋,還不能保證必定能查出在判斷的條件中存在的錯誤。所以,還須要更強的邏輯覆蓋準則去檢驗判斷內部條件。

    條件覆蓋:執行足夠的測試用例,使程序中每一個判斷的每一個條件的每一個可能取值至少執行一次;條件覆蓋深刻到斷定中的每一個條件,但可能不能知足斷定覆蓋的要求。

    斷定/條件覆蓋:執行足夠的測試用例,使得斷定中每一個條件取到各類可能的值,並使每一個斷定取到各類可能的結果。斷定/條件覆蓋有缺陷。從表面上來看,它測試了全部條件的取值。可是事實並不是如此。每每某些條件掩蓋了另外一些條件。會遺漏某些條件取值錯誤的狀況。爲完全地檢查全部條件的取值,須要將斷定語句中給出的複合條件表達式進行分解,造成由多個基本斷定嵌套的流程圖。這樣就能夠有效地檢查全部的條件是否正確了。

    條件組合覆蓋:執行足夠的例子,使得每一個斷定中條件的各類可能組合都至少出現一次。這是一種至關強的覆蓋準則,能夠有效地檢查各類可能的條件取值的組合是否正確。它不但可覆蓋全部條件的可能取值的組合,還可覆蓋全部判斷的可取分支,但可能有的路徑會遺漏掉。測試還不徹底。

1. getCharCount()函數

根據上面的函數流程圖,設計瞭如下四個文件,使其儘量的覆蓋全部的分支。
** 測試數據:**

  • text1.txt:空文件 字符數爲0
  • text2.txt:純中文文件 字符數爲0
  • text3.txt:純英文文件字符數爲80
  • text4.txt:包含了英文、中文、回車換行符、製表符的文件字符數爲73

測試代碼:

@Test
 public void testGetCharCount() throws IOException {
     FileDeal fd = new FileDeal();
     String text1 = fd.FileToString("text/text1.txt");   
     String text2 = fd.FileToString("text/text2.txt");   
     String text3 = fd.FileToString("text/text3.txt");
     String text4 = fd.FileToString("text/text4.txt");
 
     
     WordDeal wd1 = new WordDeal(text1);
     WordDeal wd2 = new WordDeal(text2);
     WordDeal wd3 = new WordDeal(text3);
     WordDeal wd4 = new WordDeal(text4);

     int cn1 = wd1.getCharCount();
     int cn2 = wd2.getCharCount();
     int cn3 = wd3.getCharCount();
     int cn4 = wd4.getCharCount();
     
     
     assertEquals(0, cn1);
     assertEquals(0, cn2);
     assertEquals(80, cn3);
     assertEquals(73, cn4);
 
 }

各個函數的測試結果:

2. 分支覆蓋率截圖:


7.效能分析

效能分析中選用了一篇大小爲2MB左右的英文小說,在測試代碼中重點測試了字符串處理類中的五個函數的運行時間。性能測試的代碼以下:

public class Perf_analy {

    FileDeal fd;
    String text1;
    WordDeal wd1;
    Map<String, Integer> wf1;
    List lis1;

    @Before
    public void setUp() throws Exception { // 初始化數據
        fd = new FileDeal();
        text1 = fd.FileToString("LittlePrince.txt");
        wd1 = new WordDeal(text1);
        wf1 = wd1.getWordFreq();
        lis1 = wd1.sortMap(wf1);

    }

    @Test
    public void testGetCharCount() throws IOException { // 測試統計字符數量函數

        long startTime = System.currentTimeMillis();
        wd1.getCharCount();
        long endTime = System.currentTimeMillis();
        System.out.println("getCharCount函數的運行時間爲" + (endTime - startTime) + "毫秒");

    }

    @Test
    public void testGetWordCount() throws IOException { // 測試統計單詞數量函數
        long startTime = System.currentTimeMillis();
        wd1.getWordCount();
        long endTime = System.currentTimeMillis();
        System.out.println("getWordCount函數的運行時間爲:" + (endTime - startTime) + "毫秒");

    }

    ………………
    ………………
    ………………
}

最後運行獲得的每一個函數的運行時間以下圖所示:

能夠看出相比於其餘函數,統計單詞個數的函數的運行時間要多得多。因而查看相應函數進行效能的改進:

public int getWordCount() // 統計單詞總數(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。)
    {
        String t = text;
        t = t.replace('\n', ' ');
        t = t.replace('\r', ' ');
        t = t.replace('\t', ' ');
        String[] spWord = t.split(" +"); // 對字符串進行分詞操做
        for (int i = 0; i < spWord.length; i++) {
            if (spWord[i].length() < 4) { // 判斷長度是否大於等於4
                continue;
            } else {
                int flag = 1; // 判斷字符串的前四位是不是英文字母
                char[] ch = spWord[i].toCharArray();
                for (int j = 0; j < 4; j++) {
                    if (!(ch[j] >= 'A' && ch[j] <= 'Z' || ch[j] >= 'a' && ch[j] <= 'z')) {
                        flag = 0;
                    }
                }
                if (flag == 1) {
                    wordCount++;

                }
            }
        }
        return wordCount;
    }

對於上面代碼,在改進時考慮到在分詞操做時可使用\\s來進行空白字符的匹配,這樣就能夠省去使用replace()函數所須要的時間,再一個就是在循環判斷前四個字符是否爲英文時使用了效率更高charAt代替了toCharArray(),改進後的代碼以下:

public int getWordCount() // 統計單詞總數(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。)
    {
        String t = text;
        String[] spWord = t.split("\\s"); // 對字符串進行分詞操做
        for (int i = 0; i < spWord.length; i++) {
            if (spWord[i].length() < 4) { // 判斷長度是否大於等於4
                continue;
            } else {
                int flag = 1; // 判斷字符串的前四位是不是英文字母
                char c;
                for (int j = 0; j < 4; j++) {
                    c = spWord[i].charAt(j);
                    if (!(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
                        flag = 0;
                    }
                }
                if (flag == 1) {
                    wordCount++;
                }
            }
        }
        return wordCount;
    }

再次運行效能分析文件,結果以下,能夠看出運行時間上已經有了明顯的減小。


8.心得體會

​ 此次的實驗其實暴露出了個人不少不足的地方,以前的做業我通常都是以代碼爲核心,我的習慣一拿到題目就立刻開始寫代碼,但此次要求是要按照流程來進行一步步的操做。最開始的對PSP表格的估計就耗費了蠻多時間,由於以前沒有過按照規範來作的項目,因此對每個模塊應該用多少時間在觀念上仍是比較模糊,最後致使實際和預估時間相差還蠻大的。

​ 在整個做業中錯誤處理和單元測試部分是我以爲最難的部分了,在上面花費的時間也是全部部分中最多的,可是最後的完成的效果仍是不太滿意,對於異常,我一貫是不多會考慮,在寫代碼的時候大部分也是按照理想的輸入狀態來,因此在對異常處理的部分仍是有不少不足。至於單元測試,可能對於測試樣例的設計仍是不太熟悉,因此會有冗餘現象。

​ 不過此次做業給個人收穫仍是很大的,學到了比較規範的項目流程,發現了本身不少細節地方的欠缺,還了解到不少新知識。

相關文章
相關標籤/搜索