1. 程式人生 > >【現代軟件工程】第一次作業——詞頻統計

【現代軟件工程】第一次作業——詞頻統計

idt hash 統計字符 最簡 系統 設計 字符 fgetc 需求

目錄

  1.1基本功能

  1.2設計實現

  1.3代碼結構

  1.4測試運行

  1.5性能分析

  1.6項目總結

  1.7 PSP展示

1.1 基本功能

  1. 統計文件的字符數(只需要統計Ascii碼,漢字不用考慮,換行符不用考慮,‘\0‘不用考慮)(ascii碼大小在[32,126]之間)

  2. 統計文件的單詞總數

  3. 統計文件的總行數(任何字符構成的行,都需要統計)(不要只看換行符的數量,要小心最後一行沒有換行符的情形)(空行算一行)

  4. 統計文件中各單詞的出現次數,輸出頻率最高的10個。

  5. 對給定文件夾及其遞歸子文件夾下的所有文件進行統計

  6. 統計兩個單詞(詞組)在一起的頻率,輸出頻率最高的前10個。

  7. 在Linux系統下,進行性能分析,過程寫到blog中(附加題)

 關於字符、行數與單詞的統計規則詳情請見:http://www.cnblogs.com/denghp83/p/8627840.html

1.2 設計實現

 1.2.1 解題思路

  1. 用命令行參數輸入文件路徑,判斷其是單個文件還是文件夾。

      若為單個文件則直接打開,統計字符、單詞與行數;若為文件夾,則遞歸遍歷該文件夾下的所有文件進行統計。

      之前用文件操作用得較少,所以對遍歷文件夾的操作不熟悉,在網上找了點資料。

學習筆記:

  1)存儲文件各種信息的結構體中:unsigned attrib表示文件的屬性,

_A_SUBDIR表示文件夾屬性。

  2)_findfirst函數

    long _findfirst( char*filespec,struct _finddata_t *fileinfo );

    返回值:如果查找成功的話,將返回一個long型的唯一的查找用的句柄(就是一個唯一編號)。這個句柄將在_findnext函數中被使用。若失敗,則返回-1。

fileinfo :這裏就是用來存放文件信息的結構體的指針。這個結構體必須在調用此函數前聲明,不過不用初始化,只要分配了內存空間就可以了。函數成功後,函數會把找到的文件的信息放入這個結構體中。

  3)_findnext函數

int _findnext( long handle, struct_finddata_t *fileinfo );

返回值:若成功返回0,否則返回-1。

參數:handle:即由_findfirst函數返回回來的句柄。

fileinfo:文件信息結構體的指針。找到文件後,函數將該文件信息放入此結構體中。

  https://blog.csdn.net/aoshilang2249/article/details/37819159)

    2. 對於字符、行數與單詞數統計,將字符數、行數與單詞數作為全局變量,一開始最簡單的想法是分三次讀取文件,後來想想真的太費時間了。

     最後的方案是每讀一個文件,就把字符、行數與單詞數統計好。

    3. 對於單詞和詞組頻率的統計:創建兩個哈希表,使用ELFHASH哈希算法計算索引值,使用拉鏈法處理沖突。

     對詞組頻率的統計一開始沒有什麽頭緒,後來翻看了其他同學的博客,通過操作前後兩個單詞的結構體指針來實現詞組在哈希表中的存儲,才大概有了點頭緒。

    4. 輸出頻率前十的單詞和詞組:通過遍歷哈希表實現。

  1.2.2 實現細節

    單詞和詞組的結構體:

typedef struct wordnode {

    int times;

    char word[MAX];//單詞原型

    char wordhash[MAX];//去掉最末尾數字且字母全為小寫

    struct wordnode *next;

}wordnode, *wordlist;



typedef struct phrasenode {

    int times;

    wordlist wordpre;//前一個單詞

    wordlist wordaft;//後一個單詞

    struct phrasenode *next;

}phrasenode, *phraselist;

    1. 字符數統計:ASCII碼值在32-126之間的字符,則字符數加一。

    2. 行數統計:掃描到‘\n‘,則行數加一。每個文件掃描結束,行數再加一。(自我感覺這個統計方法有點不太靠譜。)

    3. 單詞數統計:當掃描到分隔符後的第一個字母或數字時,開始將該字符存儲到緩沖數組,直到遇到下一個分隔符。

           再對緩沖數組中的字符串做分析,如果長度(不含末尾‘\0‘)大於等於4並且前四個字符都為字母,則單詞數加一。

           為了便於計算哈希算法的鍵值和處理沖突,將字符串做一些處理,去掉最末尾的數字並將剩下的均轉化為小寫。

           根據鍵值與字符串的大小比較在哈希表中查找,若查找失敗,則創建一個新結點;若查找成功,則次數加一。

    4. 詞組數統計:按上述方法記錄一下每一個單詞的結構體指針。

           若不是該文件的第一個單詞,將它與上一個單詞合在一起生成一個哈希鍵值,用與統計詞頻相似的方法處理哈希表。

    

for (i = 0; ((ch >= a&&ch <= z) || (ch >= A&&ch <= Z) || (ch >= 0&&ch <= 9)) && ch != EOF; i++)
            {
                charactercount++;
                buffer[i] = ch;
                ch = fgetc(fp);
            }
            charactercount--;
            buffer[i] = \0;
            if (i >= 4 && ((buffer[0] >= a&&buffer[0] <= z) || (buffer[0] >= A&&buffer[0] <= Z)) && ((buffer[1] >= a&&buffer[1] <= z) || (buffer[1] >= A&&buffer[1] <= Z)) && ((buffer[2] >= a&&buffer[2] <= z) || (buffer[2] >= A&&buffer[2] <= Z)) && ((buffer[3] >= a&&buffer[3] <= z) || (buffer[3] >= A&&buffer[3] <= Z)))
            {
                wordtotal++;//此時i即為單詞原始長度
                for (k = i - 1; ; k--)
                {
                    if ((buffer[k] >= a&&buffer[k] <= z) || (buffer[k] >= A&&buffer[k] <= Z))
                        break;//k represents the last location of a character
                }
                //my_strlwr(regular, buffer, k + 1);
                current = wordFrequency(buffer, k + 1);
                if (wordtotal > 0)
                {
                    //不是第一個單詞
                    phraseFrequency(last, current);
                }
                last = current;
            }

1.3 代碼結構

  技術分享圖片

  詳細代碼地址:https://github.com/EstherXr/learngit/blob/master/homework1.cpp

1.4 測試運行

  1. 助教給的測試集

  上面為我的結果,下面為助教給的測試結果。

  技術分享圖片

  技術分享圖片

  頻率前十的單詞和詞組及頻率與助教的結果相同,但是字符數、行數與單詞數都有偏差。對於單詞數,我覺得是各人的定義不同,比如ab123abcd中的abcd到底算不算單詞。

  2. 空文件:

  技術分享圖片

  3. 遍歷文件夾測試一:

  技術分享圖片

  4. 遍歷文件夾測試二:

  技術分享圖片

  5. 單文件輸出所有單詞:

  技術分享圖片

1.5 性能分析

  CPU總使用情況

  技術分享圖片

  遍歷文件夾函數:

 技術分享圖片

   統計字符數、行數與單詞數:

  技術分享圖片

  分析:

  從函數的CPU使用情況來看,大部分時間都花費在遍歷文件夾與統計函數上。

1.6 項目總結

  到真正寫代碼和做東西的時候,就會發現自己會的東西真的太少了。(所以這次基本是用純C寫的)也因為之前寫代碼寫得太少了,所以對自己能力的估計也很不準確,導致規劃的效率很低。

  在交代碼的那天晚上才開始做移植,但是在Linux系統上測試一直有問題,所以最後只好交了一份沒有移植的代碼。之後要把這個問題搞明白,也要開始學習如何使用虛擬機。

  以後做項目要多些文檔,可以幫助自己梳理思路,更有條理。這也是這次作業不足的地方

  最後,一定要和身邊的人多交流。

1.7 PSP展示

預估耗時/min 實際耗時/min
Planning 計劃 30 45
-Estimate -估計這個任務需要多少時間 30 45
Development 開發 1220 1600
-Analysis -需求分析 120 60
-Design Spec -設計文檔 90 60
-Design Review -設計復審 30 20
-Coding Standard -代碼規範 20 20
-Design -具體設計 120 240
-Coding -具體編碼 600 900
-Code Review -代碼復審 60 60
-Test -測試 180 240
Reporting 報告 180 265
-Test Report -測試報告 90 180
-Size Measurement -計算工作量 60 40
-Postmortem -總結反思 30 45
1430 1910

【現代軟件工程】第一次作業——詞頻統計