本文使用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;
}