英文單詞統計程序

本文使用hash表實現一個英文單詞統計程序,輸入英文段落文件EnglishFile.txt,統計結果按順序輸出到Stat_result.txt 文件當中。

特別聲明,小程序在VC6.0中驗證通過。


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/* *********************************************************
 * 文件名:  英文單詞統計程序
 * 基本需求:利用有限狀態機實現英文文件的單詞統計
 * 作者:    韓立忠
 * 創建時間:2011.07.17
/* ******************************************************* */


/* ******************** 定義頭文件 *********************** */

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

/* ********************** 宏定義 ************************* */

#define HASH_SIZE  (26)               // hash表索引大小
#define WORD_LEN   (32)               // 存放單詞的緩衝長度

/* ******************* 定義數據結構 ********************** */

// hash衝突鏈單詞結點
typedef struct dNode_s
{
 struct dNode_s *prev;
 struct dNode_s *next;
 char   *word;
 unsigned int count;   // 重複單詞計數
}dNode_t;

// hash衝突鏈頭結點
typedef struct dList_s
{
 dNode_t *head;
 dNode_t *tail;
 unsigned int length;  // 衝突鏈長度
}dList_t;

// 字符類型的枚舉
typedef enum
{
 LETTER = 0,        // 字母
 NUMBER,            // 數字 
 COMM_PUNCT,        // 常用標點,包括(, . : ; ! ?)
 DOUBLE_CHAR,       // 雙字符,包括('' "" () [] {} <>)
 OTH_VISI_CHAR,     // 除上述的其它可顯字符
 SPACE_OR_ENTER,    // 空格,tab或回車
 TOTAL_TYPE = SPACE_OR_ENTER
};

/* ******************* 定義全局變量 ********************* */

unsigned int g_StatResult[TOTAL_TYPE] = {0};  // 各種類型的統計結果

/* ********************* 函數聲明 *********************** */

void StatisticFunc(dList_t *);        // 英文單詞統計主函數

unsigned int AnalyzeChar(char);       // 分析字符函數
unsigned int HashFunc(char *);        // hash索引計算函數
void InitHash(dList_t *);             // hash表初始化函數
void AddWord2Hash(dList_t *, char *); // hash緩存單詞插入函數
void PrintFunc(dList_t *);            // hash緩存遍歷打印函數
void FreeHash(dList_t *);             // hash緩存釋放函數


void main(void)

 dList_t hashTable[HASH_SIZE];
 InitHash(hashTable);

 StatisticFunc(hashTable);
 
 PrintFunc(hashTable);
 FreeHash(hashTable);
}

/* 統計函數:統計英文單詞數並存於hash表中,對於其餘各種字符僅統計個數 */
void StatisticFunc(dList_t *table)
{
 FILE *fp = NULL;
 char ch = 0;
 char flag = 0;          // 1標識遇到單引號,0代表讀完或未遇到單引號
 char *word = NULL;
 unsigned int wordLen = 0;

 fp = fopen("EnglishFile.txt", "rt");
 if (NULL == fp)
    {
  printf("StatisticFunc:Cannot Open File!\n");
  exit(0);
    }

 word = (char *)malloc(WORD_LEN * sizeof(char));
 if (NULL == word)
 {
  printf("StatisticFunc:Insufficient Memory!\n");
  exit(0);
 }
 memset(word, 0, WORD_LEN * sizeof(char));
 
 ch = fgetc(fp);
 while (ch != EOF)
 {
  switch (AnalyzeChar(ch))
  {
  case LETTER:            // 字母
   {
    wordLen = 0;
    do
    {
     /* 若在單詞首遇到字符',則視其爲單引號,且讀此單詞過程中遇到的'視爲
        另一半單引號,並確定已讀完完整單詞;否則,視字符'爲單詞縮寫符 */
     if ((1 == flag) && ('\'' == ch))
     {
      flag = 0;
      g_StatResult[DOUBLE_CHAR]++;
      ch = fgetc(fp);
      break;
     }

     if ((ch >= 'A') && (ch <= 'Z'))
     {
      ch += 32;
     }
     *(word + wordLen) = ch;
     ch = fgetc(fp);
     wordLen++;
    }while ((LETTER == AnalyzeChar(ch)) || ('-' == ch) || ('\'' == ch));
    *(word + wordLen) = '\0';

    AddWord2Hash(table, word);
    g_StatResult[LETTER]++;
    break;
   }
  case NUMBER:            // 數字
   {
    do
    {
     ch = fgetc(fp);
    }while (NUMBER == AnalyzeChar(ch));
    g_StatResult[NUMBER]++;
    break;
   }
  case COMM_PUNCT:        // 常見標點符號
   {
    g_StatResult[COMM_PUNCT]++;
    ch = fgetc(fp);
    break;
   }
  case DOUBLE_CHAR:       // 雙字符,單詞首遇到單引號,flag置1
   {
    if ('\'' == ch)
    {
     flag = 1;
    }
    g_StatResult[DOUBLE_CHAR]++;
    ch = fgetc(fp);
    break;
   }
  case OTH_VISI_CHAR:     // 其他可顯字符
   {
    g_StatResult[OTH_VISI_CHAR]++;
    ch = fgetc(fp);
    break;
   }
  case SPACE_OR_ENTER:    // 空格,tab或回車
   {
    ch = fgetc(fp);
    break;
   }
  default:
   {
    break;
   }
  }
 }
 g_StatResult[DOUBLE_CHAR] /= 2;  // 雙字符總數,爲計數結果除以2

 fclose(fp);
 free(word);
}

/* 簡單分析字符的類型 */
unsigned int AnalyzeChar(char ch)
{
 if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
 {
  return LETTER;
 }
 else if ((ch >= '0') && (ch <= '9'))
 {
  return NUMBER;
 }
 else if ((',' == ch) || ('.' == ch) || ('?' == ch) || ('!' == ch) || (':' == ch) || (';' == ch))
 {
  return COMM_PUNCT;
 }
 else if (('\'' == ch) || ('"' == ch) || ('(' == ch) || (')' == ch) ||
  ('[' == ch) || (']' == ch) || ('{' == ch) || ('}' == ch) || ('<' == ch) || ('>' == ch))
 {
  return DOUBLE_CHAR;
 }
 else if ((ch != ' ') && (ch != '\n') && (ch != '\t'))  // 其他可顯字符統計
 {
  return OTH_VISI_CHAR;
 }
 else
 {
  return SPACE_OR_ENTER;
 }
}

/* hash表初始化函數 */
void InitHash(dList_t *table)
{
 unsigned int idx = 0;
 if (NULL == table)
 {
  printf("InitHash:Insufficient Memory!\n");
  exit(0);
 }

 //memset(table, 0, HASH_SIZE * sizeof(dList_t))

 for (idx = 0; idx < HASH_SIZE; idx++)
 {
  table[idx].head   = NULL;
  table[idx].tail   = NULL;
  table[idx].length = 0;
 }
}

/* hash表單詞緩存,升序插入單詞;對於重複出現的單詞僅計數重複次數,不執行插入*/
void AddWord2Hash(dList_t *table, char *word)
{
 unsigned int idx = 0;
 unsigned int buffLen = 0;
 dNode_t *pTmpNode = NULL;
 dNode_t *pNewNode = NULL;
 if ((NULL == table) || (NULL == word))
 {
  printf("AddWord2Hash:Insufficient Memory!\n");
  exit(0);
 }

 idx = HashFunc(word);
 if (idx >= HASH_SIZE)
 {
  printf("AddWord2Hash:HashKey Error!\n");
  exit(0);
 }

 pNewNode = (dNode_t *)malloc(sizeof(dNode_t));
 if (NULL == pNewNode)
 {
  printf("AddWord2Hash:Insufficient Memory!\n");
  exit(0);
 }
 
 /* 保證存放單詞的內存是4-byte的整數倍,且末尾爲'\0' */
 buffLen = (strlen(word)/sizeof(int) + 1) * sizeof(int);
 pNewNode->word = (char *)malloc(buffLen);
 if (NULL == pNewNode->word)
 {
  free(pNewNode);
  printf("AddWord2Hash:Insufficient Memory!\n");
  exit(0);
 }
 memset(pNewNode->word, 0, buffLen);
 pNewNode->prev  = NULL;
 pNewNode->next  = NULL;
 pNewNode->count = 1;
 strcpy(pNewNode->word, word);

 if (NULL == table[idx].head)   // idx處衝突鏈爲空
 {
  table[idx].head = pNewNode;
  table[idx].tail = pNewNode;
  (table[idx].length)++;
 }
 else                           // idx處衝突鏈非空
 {
  pTmpNode = table[idx].head;
  while (pTmpNode != NULL)
  {
   if (strcmp(word, pTmpNode->word) > 0)       // 比較結果大於0,繼續循環
   {
    pTmpNode = pTmpNode->next;
   }
   else if (0 == strcmp(word, pTmpNode->word)) // 若單詞重複,計算值加1,不執行插入
   {
    (pTmpNode->count)++;
    free(pNewNode);
    return;
   }
   else                                        // 找到插入位置
   {
    if (NULL == pTmpNode->prev)  // 插在衝突鏈首結點位置
    {
     table[idx].head = pNewNode;
    }
    else                         // 插在衝突鏈中間節點位置
    {
     pTmpNode->prev->next = pNewNode;
     pNewNode->prev = pTmpNode->prev;
    }
    pNewNode->next = pTmpNode;
    pTmpNode->prev = pNewNode;
    (table[idx].length)++;
    return;
   }
  }
  if (NULL == pTmpNode)                // 插在衝突鏈尾節點位置
  {
   (table[idx].tail)->next = pNewNode;
   pNewNode->prev = table[idx].tail;
   table[idx].tail = pNewNode;
   (table[idx].length)++;
  }
 }
}

/* hash緩存釋放函數,從衝突鏈頭開始釋放;hash表爲棧內存,由系統自動釋放 */
void FreeHash(dList_t *table)
{
 unsigned int idx = 0;
 dNode_t *pTmpNode = NULL;
 if (NULL == table)
 {
  printf("FreeHash:Insufficient Memory!\n");
  exit(0);
 }

 for (idx = 0; idx < HASH_SIZE; idx++)
 {
  table[idx].length = 0;
  pTmpNode = table[idx].head;
  while (pTmpNode != NULL)
  {
   table[idx].head = pTmpNode->next; // 記錄新的衝突鏈頭
   if (pTmpNode->next != NULL)
   {
    pTmpNode->next->prev = NULL;
   }

   free(pTmpNode->word);
   free(pTmpNode);
   pTmpNode = table[idx].head;
  }
 }
}

/* 打印函數:文件打印 */
void PrintFunc(dList_t *table)
{
 FILE *fp = NULL;
 unsigned int idx = 0;
 dNode_t *pTmpNode = NULL;
 if (NULL == table)
 {
  printf("PrintFunc:Insufficient Memory/Table isnot Existing!\n");
  exit(0);
 }

 fp = fopen("Stat_result.txt", "w");
 if (NULL == fp)
 {
  printf("PrintFunc:Cannot Creat a file!\n");
  exit(0);
 }

 fprintf(fp, "==== STAT. RESULTS ====\n\n");
 fprintf(fp, "Total  Words: %6d\n", g_StatResult[LETTER]);
 fprintf(fp, "Total Puncts: %6d\n", g_StatResult[COMM_PUNCT]);
 fprintf(fp, "Total Number: %6d\n", g_StatResult[NUMBER]);
 fprintf(fp, "Total DbChar: %6d\n", g_StatResult[DOUBLE_CHAR]);
 fprintf(fp, "Total  Other: %6d\n", g_StatResult[OTH_VISI_CHAR]);
 fprintf(fp, "\n\n======== WORD LIST ========\n");
 fprintf(fp, "\nWORD             FREQUENCY\n\n");

 for (idx = 0; idx < HASH_SIZE; idx++)
 {
  pTmpNode = table[idx].head;
  while (pTmpNode != NULL)
  {
   fprintf(fp, "%-20s%2d\n", pTmpNode->word, pTmpNode->count);
   pTmpNode = pTmpNode->next;
  }
  if (table[idx].head != NULL)
  {
   fprintf(fp, "\n");
  }
 }
 fprintf(fp, "========= THE END =========\n");

 fclose(fp);
}

/* hash函數,取單詞的首字母與字符a的差值爲hash表的索引值 */
unsigned int HashFunc(char *key)
{
 unsigned int idx = 0;
 if (NULL == key)
 {
  printf("HashFunc:Insufficient Memory!\n");
  exit(0);
 }

 idx = (int)(*key - 'a');
 return idx;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章