統計漢字使用頻率

一、主要內容

1、學習什麼是hash表,運用hash表解決問題。

2、瞭解自動數組(根據存儲內容自動調整大小),數組的排序。

3、利用hash表統計經典鉅著《紅樓夢》中所有漢字的使用次數。

4、利用數組,對所有漢字按使用次數進行排序。

二、要到的基礎庫

本練習要到Glib庫,Glib是在C語言標準庫的基礎上實現的一套跨平臺的功能強大的通用函數庫。

三、編程環境

編程環境爲Cygwin,安裝方法見https://blog.csdn.net/sudsheng/article/details/88909066

四、實現步驟

1、識別漢字

漢字通常採用GBK編碼,GBK編碼標準如下(https://www.qqxiuzi.cn/zh/hanzi-gbk-bianma.php


GBK編碼範圍:8140-FEFE,漢字編碼範圍見第二節:碼位分配及順序。

GBK編碼,是對GB2312編碼的擴展,因此完全兼容GB2312-80標準。GBK編碼依然採用雙字節編碼方案,其編碼範圍:8140-FEFE,剔除xx7F碼位,共23940個碼位。共收錄漢字和圖形符號21886個,其中漢字(包括部首和構件)21003個,圖形符號883個。GBK編碼支持國際標準ISO/IEC10646-1和國家標準GB13000-1中的全部中日韓漢字,幷包含了BIG5編碼中的所有漢字。GBK編碼方案於1995年12月15日正式發佈,這一版的GBK規範爲1.0版。

一、字彙

GBK 規範收錄了 ISO 10646.1 中的全部 CJK 漢字和符號,並有所補充。具體包括:
1. GB 2312 中的全部漢字、非漢字符號。
2. GB 13000.1 中的其他 CJK 漢字。以上合計 20902 個 GB 化漢字。
3. 《簡化字總表》中未收入 GB 13000.1 的 52 個漢字。
4. 《康熙字典》及《辭海》中未收入 GB 13000.1 的 28 個部首及重要構件。
5. 13 個漢字結構符。
6. BIG-5 中未被 GB 2312 收入、但存在於 GB 13000.1 中的 139 個圖形符號。
7. GB 12345 增補的 6 個拼音符號。
8. 漢字“〇”。
9. GB 12345 增補的 19 個豎排標點符號(GB 12345 較 GB 2312 增補豎排標點符號 29 個,其中 10 個未被 GB 13000.1 收入,故 GBK 亦不收)。
10. 從 GB 13000.1 的 CJK 兼容區挑選出的 21 個漢字。
11. GB 13000.1 收入的 31 個 IBM OS/2 專用符號。
12.未錄入《新華字典》上的一些字,如“韡”的簡體。

二、碼位分配及順序

GBK 亦採用雙字節表示,總體編碼範圍爲 8140-FEFE,首字節在 81-FE 之間,尾字節在 40-FE 之間,剔除 xx7F 一條線。總計 23940 個碼位,共收入 21886 個漢字和圖形符號,其中漢字(包括部首和構件)21003 個,圖形符號 883 個。

全部編碼分爲三大部分:

1. 漢字區。包括:
a. GB 2312 漢字區。即 GBK/2: B0A1-F7FE。收錄 GB 2312 漢字 6763 個,按原順序排列。
b. GB 13000.1 擴充漢字區。包括:
(1) GBK/3: 8140-A0FE。收錄 GB 13000.1 中的 CJK 漢字 6080 個。
(2) GBK/4: AA40-FEA0。收錄 CJK 漢字和增補的漢字 8160 個。CJK 漢字在前,按 UCS 代碼大小排列;增補的漢字(包括部首和構件)在後,按《康熙字典》的頁碼/字位排列。
(3) 漢字“〇”安排在圖形符號區GBK/5:A996。

2. 圖形符號區。包括:
a. GB 2312 非漢字符號區。即 GBK/1: A1A1-A9FE。其中除 GB 2312 的符號外,還有 10 個小寫羅馬數字和 GB 12345 增補的符號。計符號 717 個。
b. GB 13000.1 擴充非漢字區。即 GBK/5: A840-A9A0。BIG-5 非漢字符號、結構符和“〇”排列在此區。計符號 166 個。

3. 用戶自定義區:分爲(1)(2)(3)三個小區。
(1) AAA1-AFFE,碼位 564 個。
(2) F8A1-FEFE,碼位 658 個。
(3) A140-A7A0,碼位 672 個。
第(3)區儘管對用戶開放,但限制使用,因爲不排除未來在此區域增補新字符的可能性。

三、字形

GBK 對字形作了如下的規定:
1. 原則上與 GB 13000.1 G列(即源自中國大陸法定標準的漢字)下的字形/筆形保持一致。
2. 在 CJK 漢字認同規則的總框架內,對所有的 GBK 編碼漢字實施“無重碼正形”(“GB 化”);即在不造成重碼的前提下,儘量採用中國新字形。
3. 對於超出 CJK 漢字認同規則的、或認同規則尚未明確規定的漢字,在 GBK 碼位上暫安放舊字形。這樣,在許多情況下 GBK 收入了同一漢字的新舊兩種字形。
4. 非漢字符號的字形,凡 GB 2312 已經包括的,與 GB 2312 保持一致;超出 GB 2312 的部分,與 GB 13000.1 保持一致。
5. 帶聲調的拼音字母取半角形式。


由以上紅色字體可知,每個漢字佔兩個字節,取值範圍爲

B0A1-F7FE

8140-A0FE

AA40-FEA0

第一個字節按有符號數處理時爲負數。

2、選擇合適的容器存放漢字以及其出現的次數

統計過程如下:

讀取一個漢字 —> 在容器中查找該漢字 —> 如果查找到該漢字則將該漢字的計數加一;如果沒有查找到則將該漢字新加入容器並將計數設置爲1

整個過程中,在容器中查找漢字將是最費時的部分,所以要選擇查找速度快的容器,hash表無疑是最合適的。

3、對漢字按次數進行排序

hash表中存的漢字是無序的並且不能排序,如果需要按漢字使用的次數進行排序,則需要另一種容器數組或者鏈表。對於我們的問題數組和鏈表都可以,但是數組相對來講需要的內存更少並且可以預先分配內存,更爲合適。

漢字和其次數已經存在hash表中,數組只需要保存指針即可。

4、代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <glib.h>

typedef struct _WordTimes WordTimes;

struct _WordTimes
{
    char word[3];
    int times;
};

static int cmp_func(gconstpointer a, gconstpointer b)
{
    return (*(WordTimes **)b)->times - (*(WordTimes **)a)->times;
}

static void insert_array(gpointer key, gpointer value, gpointer user_data)
{
    GPtrArray ** array = (GPtrArray **)user_data;
    WordTimes * word_times = (WordTimes *)key;

    g_ptr_array_add(*array, word_times);
}

static void free_word_times(gpointer data)
{
    g_slice_free(WordTimes, data);
}

static void print_word_times(gpointer data)
{
    printf("%s : %d\n", ((WordTimes *)data)->word, ((WordTimes *)data)->times);
}

int main(int argc, char* argv[])
{

    GHashTable * h;
    FILE * fp;
    char word[4] = {0};
    char buf[4096] = {0};
    WordTimes * word_times = NULL;
    unsigned short u16_word;
    GPtrArray * array = NULL;
    int i = 0;

    fp = fopen(argv[1], "r");

    if (NULL == fp)
    {
        printf("文件%s不存在!\n", argv[1]);
    }

    h = g_hash_table_new_full ((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal, free_word_times, NULL);

    while (NULL != fgets(buf, sizeof(buf), fp))
    {
        for (i = 0; buf[i] != 0; i ++)
        {
            if (buf[i] < 0)
            {
                word[0] = buf[i++];
                word[1] = buf[i];
                word[2] = 0;

                *(char *)&u16_word = word[1];
                *((char *)&u16_word + 1)= word[0];

                if (((u16_word >= 0xB0A1) && (u16_word <= 0xF7FE))
                        || ((u16_word >= 0x8140) && (u16_word <= 0xA0FE))
                        || ((u16_word >= 0xAA40) && (u16_word <= 0xFEA0)))
                {
                    if (g_hash_table_lookup_extended(h, word, (gpointer *)&word_times, NULL))
                    {
                        (word_times->times) ++;
                    }
                    else
                    {
                        word_times = g_slice_new(WordTimes);
                        word_times->times = 1;
                        strcpy(word_times->word, word);
                        g_hash_table_insert (h, word_times, NULL);
                    }
                }
            }
        }
    }
    fclose(fp);

    array = g_ptr_array_sized_new(4096);
    g_hash_table_foreach(h, insert_array, &array);
    g_ptr_array_sort(array, cmp_func);
    for (i = 0; i < array->len; i++)
    {
        print_word_times(g_ptr_array_index(array, i));
    }
    g_ptr_array_free(array, 0);
    g_hash_table_destroy(h);

    return 0;
}

5、運行結果

前20個                             後20個

     

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