批处理之家's Archiver

happy886rr 发表于 2017-10-25 22:45

多国语言词频、字频统计工具 trie

[i=s] 本帖最后由 happy886rr 于 2017-10-25 23:27 编辑 [/i]

[词频、字频统计工具trie]

[quote]
这是继文本工具tl之后的又一文本第三方,统计多国语言词频、字频、单词频率。可以智能的做前缀是否去重,智能粒进匹配。智能切词。速度比同类软件快100倍。

存下图为a.zip解压便是(外链图有效期仅7天,过期不再提供任何下载)
[img]http://i4.bvimg.com/604745/7f226ead5cfc225a.png[/img]


用法:
-------------------------------------------------------------------------
trie u:[统计字典文件] <[待统计文本流]>[输出文本流]

echo hello world|trie -u:C1.DI
trie +u:C2.DI <in.txt>out.txt
trie u:S2_IDIOM.DI <in.txt>out.txt
trie m

注:u前面可以加正负号,表示正逆序排序输出统计表。
-------------------------------------------------------------------------
[/quote]

原创代码:[code]/*
CONSOLE TXT CHARACTER STATISTICS TOOL @COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
WED OCT 25 2017 21:10:16 GMT+0800
**********************************************************************
gcc trie.c -o trie.exe                 REM For Windows
gcc trie.c -o trie                     REM For Linux
gcc trie.c -o trie                     REM For Android
cl  trie.cpp /MD /Ox /out:trie.exe     REM For VS
**********************************************************************
*/

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

// 定义帮助说明
#define HELP_INFORMATION "\
Trie v1.0 - Console txt character statistics tool\n\
Usage:\n\
    trie [option] <[infile]>[outfile]\n\
\n\
General options:\n\
     u:[userdic] Not sorting statistics result\n\
    +u:[userdic] Sorting result order by ASC\n\
    -u:[userdic] Sorting result order by DESC\n\
\n\
You can use switch 'm' to make a user dictionary head file\n\
"
// 定义字典注释头
#define DICFILE_HEAD "\
/|||\n\
TRIE - USER DICTIONARY TABLE 1.0\n\
==============================================================\n\
DICTIONARY NAME:  EMPTY\n\
MADE DATE:        1970-01-01\n\
CHARSET:          ANSI/UTF8\n\
MAX SHOW:\n\
==============================================================\n\
BYTE LOW:         0\n\
BYTE HIGH:        255\n\
INSERT MIN:       1\n\
INSERT MAX:       8\n\
STEP FORWARD MIN: 1\n\
STEP FORWARD MAX: 2\n\
==============================================================\n\
|||/\n\
"
#ifndef byte
typedef unsigned char byte;
#endif

#ifndef bool
typedef enum {false, true} bool;
#endif

// 比特数组结尾
#define BYTE_EOF 0xFF
// 标准行长
#define LINE_BUFF_SIZE     (1024 * 64)
// 拼接缓存区最大长度
#define MAX_JOIN_SIZE     32
// 展示数组词条容量(不超过64万条)
#define SHOW_ARRAY_SIZE    (1024 * 10 * 64)
// Trie树深度,即字典词目最大字节数
#define TRIE_DEEP_SIZE     24
// Trie树价值数组长度
#define TRIE_VALUE_SIZE    1

// Trie树启动参数
static int trie_low_offset;
static int trie_high_offset;
static int trie_min_granularity;
static int trie_max_granularity;
static int trie_min_insert;
static int trie_max_insert;
static int use_english_mode;
static int use_substring_match;

// Trie树分支宽度
static int trie_branch_size;
// 统计表数组辅助指针
static int* p_static_array;
// 展示列表数目
static int show_list_size;
// 已经展示的数目
static int already_show_list;

// 判断是否字母的宏
#define ISLETTER(x) ( ((0x41 <= (x)) && ((x) <= 0x5A)) || ((0x61 <= (x)) && ((x) <= 0x7A)) )
// 判断是否小写字母的宏
#define ISLOWERLETTER(x) ((0x61 <= (x)) && ((x) <= 0x7A))
// 判断是否大写字母的宏
#define ISUPPERLETTER(x) ((0x41 <= (x)) && ((x) <= 0x5A))

// 取最值
#define MAXVALUE(a,b) (((a)>(b))?(a):(b))

// 排序类型
typedef enum
{
        SORT_NO = 0,
        SORT_ASC = 1,
        SORT_DESC = -1
} SortMode;

// 快排堆栈尺寸
#define QSORT_STACK_SIZE   1024
// 快排数据交换
#define SWAP(a,b) (((a)==(b))?0:((a)^=(b)^=(a)^=(b)))
// #define SWAP(a,b) do{int _tmp=(a);(a)=(b),(b)=_tmp;}while(false)
// 快排比较函数
#define COMP(a,b,sortMode) (((a)-(b))*(sortMode))

// 增强型快排算法
bool QsortStaticArray(int* staticArray, SortMode sortMode, int left, int right)
{
        // 针对仅排序一个元素时,无需排序,直接返回真
        if(right == left)
        {
                return true;
        }

        // 检验输入参数合法性
        if(
            staticArray == NULL
            || left < 0
            || right < 0
            || left > right
        )
        {
                return false;
        }

        // 数据类型转化
        int (*iArray)[2] = (int(*)[2])staticArray;

        // 定义数组堆栈,用来记录
        int pStack[QSORT_STACK_SIZE][2], pStackIndex = 0;

        // 快排分区首次出栈
        pStack[pStackIndex   ][0] = left;
        pStack[pStackIndex ++][1] = right;

        // 混排算法核心
        while(pStackIndex > 0)
        {
                // 快排分区出栈
                int i = pStack[-- pStackIndex][0];
                int j = pStack[   pStackIndex][1];

                if(i < j)
                {
                        int ls = i, rs = j;

                        // 对于小数组,直接采用选择排序
                        if(j - i + 1 < 8)
                        {
                                int p, max;
                                while (ls < rs)
                                {
                                        max = ls;
                                        for (p = ls + 1; p <= rs; p ++)
                                        {
                                                if(COMP(iArray[p][0], iArray[max][0], sortMode) > 0)
                                                {
                                                        max = p;
                                                }
                                        }
                                        SWAP(iArray[max][0], iArray[rs][0]);
                                        SWAP(iArray[max][1], iArray[rs][1]);
                                        rs--;
                                }

                                continue;
                        }

                        // 计算中间项数
                        int mid = (j-i)/2 + i + 1;
                        // 整理局部数组,以使中位数居中
                        if(COMP(iArray[i][0], iArray[mid][0], sortMode) > 0)
                        {
                                SWAP(iArray[i][0], iArray[mid][0]);
                                SWAP(iArray[i][1], iArray[mid][1]);
                        }
                        if(COMP(iArray[i][0], iArray[j][0], sortMode) > 0)
                        {
                                SWAP(iArray[i][0], iArray[j][0]);
                                SWAP(iArray[i][1], iArray[j][1]);
                        }
                        if(COMP(iArray[mid][0], iArray[j][0], sortMode) > 0)
                        {
                                SWAP(iArray[mid][0], iArray[j][0]);
                                SWAP(iArray[mid][1], iArray[j][1]);
                        }
                        SWAP(iArray[mid][0], iArray[i][0]);
                        SWAP(iArray[mid][1], iArray[i][1]);

                        // 获取基准数据
                        int base[2] = {iArray[ls][0], iArray[ls][1]};
                        bool needsCheck = false;
                        bool alreadyCheck = false;

                        // 快排交换核心
                        while(ls < rs)
                        {
                                while(ls < rs && COMP(iArray[rs][0], base[0], sortMode) >= 0)
                                {
                                        rs--;
                                }
                                iArray[ls][0] = iArray[rs][0];
                                iArray[ls][1] = iArray[rs][1];

                                // 右排序检查器
                                if(alreadyCheck == false)
                                {
                                        if(rs == ls)
                                        {
                                                needsCheck = true;
                                        }
                                }

                                while(ls < rs && COMP(iArray[ls][0], base[0], sortMode) <= 0)
                                {
                                        ls++;
                                }
                                iArray[rs][0] = iArray[ls][0];
                                iArray[rs][1] = iArray[ls][1];

                                // 左排序检查器
                                if(alreadyCheck == false)
                                {
                                        if(rs == ls)
                                        {
                                                needsCheck = true;
                                        }
                                }
                                // 已经检查完毕
                                alreadyCheck = true;
                        }
                        iArray[ls][0] = base[0];
                        iArray[ls][1] = base[1];

                        // 如果需要检查排序
                        if(needsCheck == true)
                        {
                                int check = i;
                                while(check < j && COMP(iArray[check][0], iArray[check + 1][0], sortMode) <= 0)
                                {
                                        check ++;
                                }
                                if(check == j)
                                {
                                        // 已经排序,直接跳转
                                        continue;
                                }
                        }

                        // 快排分区数组堆栈实现
                        int k = ls;
                        if(i < k - 1)
                        {
                                // 快排分区入栈
                                pStack[pStackIndex   ][0] = i;
                                pStack[pStackIndex ++][1] = k-1;
                        }

                        if(k +1 < j)
                        {
                                // 快排分区入栈
                                pStack[pStackIndex   ][0] = k+1;
                                pStack[pStackIndex ++][1] = j;
                        }
                }
        }
        return true;
}

// 创建TrieNode结构体
typedef struct TrieNode
{
        // 节点词尾
        bool end;

        // 节点数据 (value[]数组用于词频统计)
        int value[TRIE_VALUE_SIZE];

        // 节点分支
        struct TrieNode** branch;

} TrieNode, *PTrieNode;

// 创建TrieNode节点
PTrieNode PTrieNodeCreate(int branchSize)
{
        // 动态分配TrieNode内存
        PTrieNode pTrieNode = (PTrieNode)malloc(1 * sizeof(TrieNode));
        // 分配内存识别
        if(pTrieNode == NULL)
        {
                // 如果开辟内存失败,直接退出
                fprintf(stderr, "Can't have enough memory\n");
                exit(1);
        }

        // 词目结尾标记
        pTrieNode->end = false;

        // 统计信息记录点
        memset((void*)pTrieNode->value, (int)NULL, TRIE_VALUE_SIZE * sizeof(int));

        // 分配Trie树节点内存
        pTrieNode->branch = (TrieNode**)calloc(branchSize, sizeof(TrieNode*));
        // 如果开辟内存失败,直接退出
        if(pTrieNode->branch == NULL)
        {
                // 如果开辟内存失败,直接退出
                fprintf(stderr, "Can't have enough memory\n");
                exit(1);
        }

        // 分配内存成功,则返回节点指针
        return pTrieNode;
}

// 释放Trie树内存
void TrieFree(PTrieNode rootPTrieNode)
{
        PTrieNode pTrie = rootPTrieNode;

        int i;
        for(i = 0; i < trie_branch_size; i ++)
        {
                if(pTrie->branch[i] != NULL)
                {
                        TrieFree(pTrie->branch[i]);
                }
        }
        free(pTrie);
}

// Trie树插入函数
int TrieInsert(PTrieNode rootPTrieNode, byte* insertData, size_t insertDataLen)
{
        // 当插入数据过深时直接拒绝
        if(insertDataLen >= TRIE_DEEP_SIZE)
        {
                // 抛出错误
                fprintf(stderr, "The dictionary tree is inserted too deep\n");
                exit(1);
        }

        PTrieNode pTrie = rootPTrieNode;
        byte* p = insertData;

        while(p - insertData < insertDataLen)
        {
                if(pTrie->branch[*p - trie_low_offset] == NULL)
                {
                        // 创建分支节点
                        pTrie->branch[*p - trie_low_offset] = PTrieNodeCreate(trie_branch_size);
                }

                // 深入下层节点
                pTrie = pTrie->branch[*p - trie_low_offset];

                // 插入词组
                if(pTrie->end == false)
                {
                        if((p + 1) - insertData == insertDataLen)
                        {
                                pTrie->end = true;
                                break;
                        }
                }

                // 指针移位
                p ++;
        }

        return 0;
}

// 创建TrieStatic返回结构体
typedef struct RetTrieStatic
{
        // 单次匹配的词目数
        int thisMatchedCount;

        // 剩余的字节数目
        int restBytesNumber;

        // 剩余的匹配位置
        byte* restMatchPtr;
} RetTrieStatic, *PRetTrieStatic;

// Trie树统计函数
int TrieStatic(PTrieNode rootPTrieNode, byte* originalDataPtr, size_t originalDataLen, bool useSubstringMatch, bool useEnglishMode, bool isJoinMatch, PRetTrieStatic pRetTrieStatic, size_t minGranularity, size_t maxGranularity)
{
        // Trie节点堆栈
        PTrieNode p_trie_stack[TRIE_DEEP_SIZE + 1];

        // 本次匹配的词目数
        int mathedCount = 0;
        // 余下的数据长度
        int restDataLen = 0;

        // 设置数据指针
        byte* searchDataPtr = originalDataPtr;

        // 循环切片匹配
        while(*searchDataPtr != BYTE_EOF)
        {
                // 字符过滤器
                while(*searchDataPtr != BYTE_EOF)
                {
                        if(
                                // 判断字节范围
                                (! ((trie_low_offset <= *searchDataPtr) && (*searchDataPtr <= trie_high_offset)))
       
                                // 或者是英文模式下,非字母
                                || ((useEnglishMode) && (! ISLETTER(*searchDataPtr)))
                        )
                        {
                                searchDataPtr ++;
                        }
                        else
                        {                               
                                // 符合字节范围的、则中断内层循环,送入外层循环
                                break;
                        }
                }

                // 辅助主指针
                PTrieNode pTrie = rootPTrieNode;
                byte* p = searchDataPtr;

                // 堆栈索引
                int p_trie_index = 0;
               
                // 声明匹配参量
                int matchLen = 0;
                bool matchAlready = false;

                // 匹配算法核心
                while(*p != BYTE_EOF)
                {
                        if(
                            // 字节过滤范围
                            (!(
                            // 字节筛选器低位
                            (*p >= trie_low_offset)

                            // 字节筛选器高位
                            && (*p <= trie_high_offset)
                            ))

                            // 匹配失败
                            || (pTrie->branch[*p - trie_low_offset] == NULL)
                        )
                        {       
                                // 如果是英文匹配模式,则会自动忽略大小写                               
                                if(
                                        (useEnglishMode)
                                        && (pTrie->branch[*p - trie_low_offset] == NULL)
                                        && (ISLETTER(*p))
                                )
                                {
                                        *p = ISUPPERLETTER(*p) ?(*p + 0x20) :(*p - 0x20);
                                        int V = (int)(*p) - trie_low_offset;

                                        // 忽略大小写匹配英文单词                                        
                                        if(! ((0 <= V) && (V < trie_branch_size) && (pTrie->branch[V] != NULL)))
                                        {
                                                break;
                                        }
                                }
                                else
                                {
                                        break;
                                }
                        }

                        // 深入下层节点
                        pTrie = pTrie->branch[*p - trie_low_offset];

                        // 下层节点入栈
                        p_trie_stack[++ p_trie_index] = pTrie;

                        // 词频统计器
                        if(pTrie->end == true)
                        {
                                // 如果英文匹配模式
                                if(useEnglishMode)
                                {
                                        if(! ISLETTER(*(p+1)))
                                        {
                                                // 标记为已匹配
                                                matchAlready = true;
                                               
                                                // 计算匹配的长度
                                                matchLen = p_trie_index;
                                                pTrie->value[0] ++;
                                                break;
                                        }
                                }
                                else
                                {
                                        pTrie->value[0] ++;
                                }
                        }

                        // 指针移位
                        p ++;
                }

                // 扫尾处理
                while((p_trie_index > 0) && (! useEnglishMode))
                {
                        // 正常匹配模式
                        if((p_trie_stack[p_trie_index])->end == true)
                        {
                                // 词条统计消重
                                if(
                                    // 并且已经匹配过
                                    (matchAlready)

                                    // 并且统计数目大于0
                                    && ((p_trie_stack[p_trie_index]->value)[0] > 0)
                                )
                                {
                                        // 之前匹配的子串去重
                                        (p_trie_stack[p_trie_index]->value)[0] --;
                                }

                                // 计算匹配到的词组长度
                                if(! matchAlready)
                                {
                                        // 标记为已匹配
                                        matchAlready = true;
                                       
                                        // 计算匹配到的词组长度
                                        matchLen = p_trie_index;
                                       
                                        // 是否启用子串统计
                                        if(! useSubstringMatch)
                                        {
                                                break;
                                        }
                                }
                        }
                        p_trie_index --;
                }

                // 根据匹配长度去结算下一数据点位
                if(matchAlready)
                {
                        // 匹配词目计数器
                        mathedCount ++;
                        // 数据指针推进
                        searchDataPtr += matchLen;
                }
                else
                {
                        if(! useEnglishMode)
                        {
                                // 数据指针粒度推进
                                searchDataPtr += ((*p) < 0x80) ?minGranularity :maxGranularity;
                        }
                        else
                        {
                                // 当为英文匹配模式时,先略过匹配失败的连续字母
                                while((*searchDataPtr != BYTE_EOF) && (ISLETTER(*searchDataPtr)))
                                {                               
                                        searchDataPtr ++;
                                }       
                        }
                }
               
                //  是否进入拼接匹配模式
                if(isJoinMatch)
                {
                        // 计算剩余数据长度
                        if(*searchDataPtr != BYTE_EOF)
                        {
                                restDataLen = originalDataLen - (searchDataPtr - originalDataPtr);
                        }
                        else
                        {
                                restDataLen = 0;
                        }

                        // 如果余下的数据过短
                        if(restDataLen + 1 < MAX_JOIN_SIZE)
                        {
                                // 直接跳出
                                break;
                        }
                }
        }

        // 装载返回值结构体
        pRetTrieStatic->thisMatchedCount = (int)mathedCount;
        pRetTrieStatic->restBytesNumber = (int)restDataLen;
        pRetTrieStatic->restMatchPtr = (searchDataPtr == originalDataPtr) ?NULL :(byte*)searchDataPtr;
        return 0;
}

// 打印Trie统计结果
static byte branch_stack[TRIE_DEEP_SIZE + 1] = {};
static int branch_stack_index = -1;

// Trie打印
int TriePrint(PTrieNode rootPTrieNode, const int* inArray, int totalMatchedNum)
{
        // 入栈
        branch_stack_index ++;

        // 辅助结构体指针
        PTrieNode pTrie = rootPTrieNode;

        int i;
        for(i = 0; i < trie_branch_size; i ++)
        {
                if(pTrie->branch[i] != NULL)
                {
                        branch_stack[branch_stack_index] = (char)(i + (int)trie_low_offset);

                        if((pTrie->branch[i])->end == true)
                        {
                                if((pTrie->branch[i])->value[0] > 0)
                                {
                                        // 置结词条结束符
                                        branch_stack[branch_stack_index + 1] = 0x00;

                                        // 如果是非排序打印
                                        if(inArray == NULL)
                                        {
                                                // 打印统计列表
                                                fprintf(stdout, "%8d    %7.4f    %s\n", (pTrie->branch[i])->value[0], (((pTrie->branch[i])->value[0] * 1.0 / totalMatchedNum) * 100.0), (char*)branch_stack);

                                                // 列表显示计数器
                                                if(
                                                    (show_list_size > 0)

                                                    // 超过设定范围,直接退出
                                                    && ((++ already_show_list) >= show_list_size)
                                                )
                                                {
                                                        exit(0);
                                                }
                                        }
                                        // 如果是排序打印
                                        else
                                        {
                                                // 分配展示字串内存
                                                byte* pShowStr = (byte*)malloc((branch_stack_index + 2) * sizeof(byte));
                                                if(pShowStr == NULL)
                                                {
                                                        // 无法分配内存
                                                        fprintf(stderr, "Can't have enough memory\n");
                                                        exit(1);
                                                }

                                                // 复制展示字串
                                                memcpy((void*)pShowStr, (const void*)branch_stack, (size_t)(branch_stack_index + 2));

                                                // 将展示字串地址存入staticArray展示数组
                                                *(p_static_array ++) = (int)((pTrie->branch[i])->value[0]);
                                                *(p_static_array ++) = (int)pShowStr;

                                                // 防御数组越界
                                                if(((int)(p_static_array - inArray))/2 + 8 >= SHOW_ARRAY_SIZE)
                                                {
                                                        // 温数组越界,直接退出
                                                        fprintf(stderr, "Array out of bounds\n");
                                                        exit(1);
                                                }
                                        }
                                }
                        }

                        // 递归打印
                        TriePrint(pTrie->branch[i], inArray, totalMatchedNum);
                }
        }

        // 出栈
        branch_stack_index --;
        return 0;
}

// 字典注释键值表
static const byte* dictionary_annotation_key[][2] =
{
        {(byte*)((int*)&show_list_size),       (byte*)"MAX SHOW:"},
        {(byte*)((int*)&trie_low_offset),      (byte*)"BYTE LOW:"},
        {(byte*)((int*)&trie_high_offset),     (byte*)"BYTE HIGH:"},
        {(byte*)((int*)&trie_min_insert),      (byte*)"INSERT MIN:"},
        {(byte*)((int*)&trie_max_insert),      (byte*)"INSERT MAX:"},
        {(byte*)((int*)&trie_min_granularity), (byte*)"STEP FORWARD MIN:"},
        {(byte*)((int*)&trie_max_granularity), (byte*)"STEP FORWARD MAX:"},
        {(byte*)((int*)&use_english_mode),     (byte*)"USE ENGLISH MODE:"},
        {(byte*)((int*)&use_substring_match),  (byte*)"USE SUBMATCH MODE:"},
        {NULL,                                 (byte*)"|||/"},
        {NULL,                                 (byte*)NULL}
};

// 获取字典启动参数函数
bool AlreadyGetStartupParameters(byte* pLineHead, byte* pLineEnd)
{
        // 空行返回假
        if(pLineHead == NULL || pLineEnd == NULL || pLineHead == pLineEnd)
        {
                return false;
        }

        // 临时变量(先存下结尾的字节)
        byte tmp = *pLineEnd;
        // 置字符串结束符
        *pLineEnd = 0x00;

        // 三级指针
        byte*** ptr = (byte***)dictionary_annotation_key;

        // 计算启动参数数组长度
        while((*(ptr + 1)) != NULL)
        {
                // 利用指针实现memcasecmp功能
                byte *lp = (byte*)pLineHead, *rp = (byte*)(*(ptr + 1));
                while((*lp != 0x00) && (*rp != 0x00))
                {
                        if(
                                (ISUPPERLETTER(*lp) ?(*lp + 0x20) :*lp) != (ISUPPERLETTER(*rp) ?(*rp + 0x20) :*rp)
                        )
                        {
                                break;
                        }
                        lp ++, rp ++;
                }

                // 如果右指针滑动到结尾,说明匹配成功
                if(*rp == 0x00)
                {
                        // 判断是否为字典注释结束符串“|||/”
                        if((*(ptr + 0)) == NULL)
                        {
                                // 还原原始数据
                                *pLineEnd = tmp;

                                // 获取参数已经结束
                                return true;
                        }

                        // 提取启动参数
                        int thisParameters = atoi((char*)((byte*)pLineHead + ((byte*)rp - (byte*)(*(ptr + 1)))));

                        // 赋值给对应键名变量
                        *((int*)(*(ptr + 0))) = (int)thisParameters;

                        // 匹配了就跳出循环
                        break;
                }

                // 三级指针推进
                ptr += sizeof(*dictionary_annotation_key) / sizeof(**dictionary_annotation_key);
        }

        // 还原原始数据
        *pLineEnd = tmp;

        // 还未获取到字典注释结束符串“|||/”
        return false;
}

// 填充字典树
int TrieFillDictionary(PTrieNode rootPTrieNode, char* inFile)
{
        //初始化一些全局参数,offset取值0 ~ 255
        trie_low_offset = 0x00;
        trie_high_offset = 0xFF;
        trie_branch_size = trie_high_offset - trie_low_offset + 1;

        // 插入长度限制阈
        trie_min_insert = 1;
        trie_max_insert = TRIE_DEEP_SIZE - 1;

        // 设置统计粒度
        trie_min_granularity = 1;
        trie_max_granularity = 1;
       
        // 是否启用英文匹配模式
        use_english_mode = false;
        use_substring_match = false;

        // 读取字典文件
        FILE* fp = fopen(inFile, "rb");
        if(fp == NULL)
        {
                fprintf(stderr, "Can't read the dictionary \"%s\"\n", inFile);
                exit(1);
        }
        // 文件流回首(非常必要)
        fseek(fp, 0, SEEK_SET);
        // 文件流置末尾
        fseek(fp, 0, SEEK_END);
        // 测量db字典文件尺寸
        int inFileLen = ftell(fp);
        // 文件流回首
        fseek(fp, 0, SEEK_SET);

        // 开辟行存容器
        const byte* inFileContainer = (byte*)malloc((inFileLen + 3) * sizeof(byte));
        if(inFileContainer == NULL)
        {
                // 无法分配内存
                fprintf(stderr, "Can't have enough memory\n");
                exit(1);
        }
        // 读入内存
        fread((void*)inFileContainer, (size_t)inFileLen, sizeof(byte), fp);
        // 关闭文件流
        fclose(fp);

        // 设置数据行指针
        byte* pLine = (byte*)inFileContainer;
        // 行末置结束符(防止行末无换行)
        pLine[inFileLen + 0] = 0x0D;
        pLine[inFileLen + 1] = 0x0A;
        pLine[inFileLen + 2] = BYTE_EOF;

        // 辅助指针
        byte* p = pLine;

        // 当前小行的长度
        int lineLen = 0;

        // 是否已经获取启动参数
        bool alreadyGetParameters = false;

        // 插入的词组数目
        int insertWordsCount = 0;

        // 获取字典启动参数的步探深度
        int getStartupParametersDeep = 0;

        // 开启主循环
        while(*p != BYTE_EOF)
        {
                // 过滤换行符
                while((*p != BYTE_EOF) && (*p != 0x0D) && (*p != 0x0A))
                {
                        // 字节筛选器
                        if(
                            // 如果已经获取到启动参数
                            (alreadyGetParameters == true)
                            &&
                            (!(
                            // 字节筛选器低位
                            (*p >= trie_low_offset)

                            // 字节筛选器高位
                            && (*p <= trie_high_offset)
                                ))
                        )
                        {
                                while((*p != BYTE_EOF) && (*p != 0x0D) && (*p != 0x0A))
                                {
                                        p ++;
                                }
                                while((*p != BYTE_EOF) && ((*p == 0x0D) || (*p == 0x0A)))
                                {
                                        p ++;
                                }
                                pLine = p;
                                continue;
                        }

                        p ++;
                }

                // 计算行长
                lineLen = p - pLine;

                // 当行长太短或太长,则抛弃
                if(
                    // 如果已经获取到启动参数
                    (alreadyGetParameters == true)

                    // 插入词组长度限制
                    && (trie_min_insert <= lineLen)
                    && (lineLen <= trie_max_insert)

                    // 系统最大长度限制
                    && ((lineLen + 1) < TRIE_DEEP_SIZE)
                )
                {
                        // 将该词行插入字典树
                        TrieInsert(rootPTrieNode, (byte*)pLine, lineLen);
                        insertWordsCount ++;
                }

                // 获取启动参数子程
                if(alreadyGetParameters == false)
                {
                        if((++ getStartupParametersDeep) > 512)
                        {
                                // 启动参数隐藏太深,无法获取
                                fprintf(stderr, "Can't get startup parameters\n");
                                exit(1);
                        }

                        // 当检测到注释结束符“|||/”时
                        if(AlreadyGetStartupParameters(pLine, p) == true)
                        {
                                // 检验已获取的参数的合法性
                                if(
                                    (! ( (0 <= trie_low_offset) && (trie_low_offset <= 255) ) )
                                    || (! ( (0 <= trie_high_offset) && (trie_high_offset <= 255) ) )
                                    || (trie_low_offset > trie_high_offset)
                                    || (trie_min_insert > trie_max_insert)
                                    || (trie_max_insert > TRIE_DEEP_SIZE)
                                    || (trie_min_granularity <= 0)
                                    || (trie_max_granularity <= 0)
                                )
                                {
                                        // 启动参数错误,直接退出
                                        fprintf(stderr, "Wrong startup trie database parameters\n");
                                        exit(1);
                                }

                                // 计算Trie节点分支数目(这是一个全局变量)
                                trie_branch_size = (trie_high_offset - trie_low_offset) + 1;

                                // 关闭获取参数开关,开始执行字典词条插入
                                alreadyGetParameters = true;
                        }
                }

                // 过滤换行符
                while((*p != BYTE_EOF) && ((*p == 0x0D) || (*p == 0x0A)))
                {
                        p ++;
                }

                // 指针行进
                pLine = p;
        }

        // 释放行存容器
        free((byte*)inFileContainer);

        // 检测是否已插入词条
        if(insertWordsCount == 0)
        {
                // 读取错误,直接退出
                fprintf(stderr, "Do not insert any words\n");
                exit(1);
        }

        return 0;
}

// 统计字典树
int TrieStaticDictionary(PTrieNode rootPTrieNode, FILE* fp)
{
        if(fp == NULL)
        {
                // 读取错误,直接退出
                fprintf(stderr, "Read stdin error\n");
                exit(1);
        }

        // 指针流位重置(非常必要)
        fseek(fp, 0, SEEK_SET);

        // 初始化匹配返回结构体
        RetTrieStatic retTrieStatic = {}, *pRetTrieStatic = &retTrieStatic;

        // 余行拼接器:|余行区域|读行区域|末端|
        byte line[MAX_JOIN_SIZE + LINE_BUFF_SIZE + 3];

        // 调用TrieStatic函数时,line的偏移量
        int offset = 0;

        // 总匹配词目
        int totalMatched = 0;

        // 是否开启拼接匹配
        bool isJoinMatchMode = false;

        // 循环读取STDIN输入流,统计词频
        while(! feof(fp))
        {
                // 每次实际读取的字节数
                size_t readLen = fread((void*)(line + MAX_JOIN_SIZE), sizeof(byte), LINE_BUFF_SIZE, fp);

                // 置byte结束符
                *(line + MAX_JOIN_SIZE + readLen) = BYTE_EOF;

                // 最后一次读取可以关闭拼接模式,因为readLen长度过小
                if(readLen < LINE_BUFF_SIZE)
                {
                        isJoinMatchMode = false;
                }

                // 在Trie中匹配数据块
                TrieStatic(rootPTrieNode, (byte*)(line + MAX_JOIN_SIZE - offset), (size_t)(readLen + offset), use_substring_match, use_english_mode, isJoinMatchMode, pRetTrieStatic, trie_min_granularity, trie_max_granularity);

                // 下次调用TrieStatic函数line的偏移值
                offset = pRetTrieStatic->restBytesNumber;

                // 是否开启拼接匹配
                if(pRetTrieStatic->restMatchPtr != NULL)
                {
                        // 拼接上次余下的数据
                        memcpy((void*)((line + MAX_JOIN_SIZE) - offset), (const void*)pRetTrieStatic->restMatchPtr, (size_t)pRetTrieStatic->restBytesNumber);
                        // 开启拼接匹配开关
                        isJoinMatchMode = true;
                }
                else
                {
                        // 关闭拼接匹配开关
                        isJoinMatchMode = false;
                }

                // 总匹配词数计数器
                totalMatched += pRetTrieStatic->thisMatchedCount;
        }

        // 返回匹配的词次
        return totalMatched;
}

// 打印统计表头
int PrintStaticHead()
{
        // 创建时间结构体
        time_t startTime;
        struct tm* timeinfo;

        // 打印统计表头
        time(&startTime);
        timeinfo=localtime(&startTime);
        fprintf
        (
            stdout
            ,
            "%s"
            "------------------------------------------\n"
            "   Times    Percent    Words\n"
            "------------------------------------------\n"
            ,
            asctime(timeinfo)
        );
        return 0;
}

// MAIN主函数入口
int main(int argc, char** argv)
{
        // 参数数目不对
        if(argc != 2)
        {
                // 第一次开关筛选
                fputs(HELP_INFORMATION, stderr);
                exit(1);
        }

        // 检测是否为-m开关
        if(strcasecmp(argv[1], "m") == 0)
        {
                // 打印dicfile头
                fputs(DICFILE_HEAD, stdout);
                exit(0);
        }

        // 是否符合规则
        char* ptr = strchr(argv[1], ':');
        if(ptr == NULL)
        {
                // 第二次开关筛选
                fputs(HELP_INFORMATION, stderr);
                exit(1);
        }

        // 设结束符,同时后移指针
        *(ptr ++) = '\0';

        // 排序枚举类
        SortMode sortMode = SORT_NO;

        // 解析开关
        if(strcasecmp(argv[1], "+u") == 0)
        {
                sortMode = SORT_ASC;
        }
        else if(strcasecmp(argv[1], "-u") == 0)
        {
                sortMode = SORT_DESC;
        }
        else if(strcasecmp(argv[1], "u") == 0)
        {
                sortMode = SORT_NO;
        }
        else
        {
                // 第三次开关筛选
                fputs(HELP_INFORMATION, stderr);
                exit(1);
        }

        // 创建Trie树根
        PTrieNode rootPTrieNode = PTrieNodeCreate((int)(0xFF - 0x00 + 1));

        // 获取启动参数,并填充Trie树
        TrieFillDictionary(rootPTrieNode, ptr);

        // 统计词条
        int totalMatchedNum = TrieStaticDictionary(rootPTrieNode, stdin);

        //  如果不排序,则直接输出
        if(sortMode == SORT_NO)
        {
                // 打印统计表头
                PrintStaticHead();

                // 将词频统计印到staticArray数组中
                TriePrint(rootPTrieNode, NULL, totalMatchedNum);

                // 释放Trie树
                TrieFree(rootPTrieNode);
                return 0;
        }
        else
        {
                // 分配展示数组内存 (占用不超过8M)
                const int* staticArray = (int*)malloc(SHOW_ARRAY_SIZE * 2 * sizeof(int));
                // 将全局指针置为展示数组首地址
                p_static_array = (int*)staticArray;

                // 将词频统计印到staticArray数组中
                TriePrint(rootPTrieNode, staticArray, totalMatchedNum);

                // 释放Trie树
                TrieFree(rootPTrieNode);

                // 计算统计到的不同词条数目
                int staticKeyCount = (p_static_array - staticArray) / 2;
                if(staticKeyCount == 0)
                {
                        // 没有匹配到任何关键词目,直接退出
                        fprintf(stderr, "Do not match any keywords\n");
                        exit(1);
                }

                // 根据抽屉原理
                if(totalMatchedNum == staticKeyCount)
                {
                        // 如果没有重复,则不需要排序
                }
                else
                {
                        // 对展示数组进行混合快排
                        if(QsortStaticArray((int*)staticArray, sortMode, 0, (staticKeyCount - 1)) == false)
                        {
                                // 如果排序失败,直接终止
                                fprintf(stderr, "Qsort the result failed\n");
                                exit(1);
                        }
                }

                // 打印统计表头
                PrintStaticHead();

                // 创建辅助指针,指向展示数组首地址
                int* pStaticArray = (int*)staticArray;

                // 打印排序后的统计表
                while(pStaticArray < p_static_array)
                {
                        // 转变数据类型
                        int staticStrTimes = (int)(*(pStaticArray ++));
                        char* staticStr = (char*)(*(pStaticArray ++));

                        // 打印统计列表
                        fprintf(stdout, "%8d    %7.4f    %s\n", staticStrTimes, ((staticStrTimes * 1.0 / totalMatchedNum) * 100.0), staticStr);

                        // 列表显示计数器
                        if(
                            (show_list_size > 0)

                            // 超过设定范围,直接退出
                            && ((++ already_show_list) >= show_list_size)
                        )
                        {
                                exit(1);
                        }

                        // 释放展示字串空间
                        if(staticStr != NULL)
                        {
                                free(staticStr);
                        }
                }
                // 释放展示数组
                free((int*)staticArray);
        }

        return 0;
}
[/code].
.
最后再附上一个自己写的成语接龙机器人,名字叫小娜(可在安卓上用C4直接编译,为节约空间,成语表 需要自己补全方可使用。)[code]/*
IDSO.EXE, IDIOM SOLITAIRE TOOL, COPYRIGHT(C)2017~2019 BY HAPPY, VERSION 1.0
**************************************************************************
gcc ./idso.c -o idso
**************************************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>

// 引入头文件
//#include "idso.h"

// 跨平台宏函数
#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
#include <windows.h>
#define IDSO_SLEEP(x) Sleep(x)
#else
#include <unistd.h>
#define IDSO_SLEEP(x) usleep(x*1000)
#endif

// 定义成语接龙舒适度 (满分100)
#define IDSO_SHELL_DIFFICULTY 16

// 标准行长
#define LINE_BUFF_SIZE   1024

// 用户输入字长
#define INPUT_BUFF_SIZE  36

// 定义hash桶深度
#define BUCKET_DEEP_SIZE 4

// 声明成语接龙数组 (在idso.h文件中定义)
static char* idso_data[];
// 定义成语接龙数组
static char* idso_data[] =
{
        NULL,
        "一丁不识",
        "一不扭众",
        "一世之雄",
        "一世龙门",
        "一丘一壑",
        "一丘之貉",
        "一丝一毫",
        "一丝不挂",
        "一丝不紊",
        "一丝不苟",
        "一丝两气",
        "一丝半粟",
        "一个半个",
        "一串骊珠",
        "一举一动",
        "一举万里",
        "一举三反",
        "一举两全",
        "一举两得",
        "一举千里",
        "一举成名",
        "一之为甚",
        "一之已甚",
        "一之谓甚",
        "一乱涂地",
        "一了百了",
        "一了百当",
        "一事不知",
        "一事无成",
        "一五一十",
        "一些半些",
        "一人之交",
};

// 计算接龙数组长度
static const int idso_data_size = sizeof(idso_data) / sizeof(idso_data[0]);

// 定义BDKR哈希模式 枚举类型
typedef enum
{
        FIRST_UC = 1, // 首字hash
        LAST_UC = -1, // 末字hash
        WHOLE_UC = 0  // 全字hash
} BdkrMode;

// 定义IdsoShellCore函数返回值枚举类型
typedef enum
{
        SHELL_USER_EXIT = 0,    // 退出SHELL
        SHELL_USER_SUCCEED = 1, // 用户获胜
        SHELL_USER_FAILED = 2,  // 用户战败
        SHELL_USER_RAND = 3     // 随机成语
} RetIdso;

// 获取UTF8汉字字节长度
int GetUcLen(unsigned char inUc)
{
        if((inUc & 0x80) == 0x00)
        {
                return 1;
        }

        int ucLen = 0;
        while((inUc & 0x80) == 0x80)
        {
                ucLen ++;
                inUc <<= 1;
        }
        return ucLen;
}

// 快排辅助宏
#define UQSORT_STACK_SIZE 1024
#define UCS2UINT(x) (unsigned int)(((unsigned char)(x)[0]<<24)|((unsigned char)(x)[1]<<16)|((unsigned char)(x)[2]<<8)|((unsigned char)(x)[3]))
#define SWAP(a,b) do{char* __tmpNumber=(a); (a)=(b), (b)=__tmpNumber;}while(0)

// 混合型快排算法
int UcsQsort(char* ucStr[], int left, int right)
{
        if(
            ucStr == NULL
            || left < 0
            || right < 0
            || left >= right
        )
        {
                return 1;
        }

        // 定义数组堆栈,用来记录
        int pStack[UQSORT_STACK_SIZE][2], pStackIndex = 0;

        // 快排分区首次出栈
        pStack[pStackIndex   ][0] = left;
        pStack[pStackIndex ++][1] = right;

        int i, j;
        while(pStackIndex > 0)
        {
                // 快排分区出栈
                i = pStack[-- pStackIndex][0];
                j = pStack[   pStackIndex][1];

                if(i < j)
                {
                        int ls = i, rs = j;
                        char* baseNum = NULL;

                        // 对于小数组,直接采用选择排序
                        if(j-i+1 < 8)
                        {
                                int p, max;
                                while (ls < rs)
                                {
                                        max = ls;
                                        for (p = ls+1; p <= rs; p ++)
                                        {
                                                if (UCS2UINT(ucStr[p]) > UCS2UINT(ucStr[max]))
                                                {
                                                        max = p;
                                                }
                                        }
                                        SWAP(ucStr[max], ucStr[rs]);
                                        rs--;
                                }

                                continue;
                        }

                        // 计算中间项数
                        int mid = (j-i)/2 + i + 1;

                        // 整理局部数组,以使中位数居中
                        if(UCS2UINT(ucStr[i]) > UCS2UINT(ucStr[mid]))
                        {
                                SWAP(ucStr[i], ucStr[mid]);
                        }
                        if(UCS2UINT(ucStr[i]) > UCS2UINT(ucStr[j]))
                        {
                                SWAP(ucStr[i], ucStr[j]);
                        }
                        if(UCS2UINT(ucStr[mid]) > UCS2UINT(ucStr[j]))
                        {
                                SWAP(ucStr[mid], ucStr[j]);
                        }
                        SWAP(ucStr[mid], ucStr[i]);

                        // 快排交换核心
                        ls = i, rs = j, baseNum = ucStr[ls];
                        while(ls<rs)
                        {
                                while(ls < rs && UCS2UINT(ucStr[rs]) >= UCS2UINT(baseNum))
                                {
                                        rs--;
                                }
                                ucStr[ls] = ucStr[rs];

                                while(ls < rs && UCS2UINT(ucStr[ls]) <= UCS2UINT(baseNum))
                                {
                                        ls++;
                                }
                                ucStr[rs] = ucStr[ls];
                        }
                        ucStr[ls] = baseNum;

                        // 快排分区数组堆栈实现
                        int k = ls;
                        if(i < k)
                        {
                                // 快排分区入栈
                                pStack[pStackIndex   ][0] = i;
                                pStack[pStackIndex ++][1] = k-1;
                        }

                        if(k < j)
                        {
                                // 快排分区入栈
                                pStack[pStackIndex   ][0] = k+1;
                                pStack[pStackIndex ++][1] = j;
                        }
                }
        }
        return 0;
}

// BKDR哈希算法(当传递参数inStrLen为1时表示获取首字hash,为-1时表示获取尾字hash,其他数表示获取全字hash)
int BKDRHash(char *inStr, BdkrMode inMode, int bkdrBase)
{
        // 空串返回直接零
        if(inStr == NULL)
        {
                return 0;
        }

        int inStrLen = -1;
        if(inMode == FIRST_UC)
        {
                inStrLen = GetUcLen((unsigned char)*inStr);
        }
        else if(inMode == LAST_UC)
        {
                inStrLen = 1;
                inStr += (int)(strlen(inStr) - 1);
                while(((*inStr) & 0xC0) == 0x80)
                {
                        inStrLen ++;
                        inStr --;
                }
        }

        unsigned int seed = 131;
        unsigned int hash = 0;

        char* str = inStr;
        while ((str - inStr < inStrLen) || ((inMode == WHOLE_UC) && (*str)))
        {
                hash = hash * seed + (*(str ++));
        }
        return (int)(hash % bkdrBase);
}

// 获取成语首字索引hash表
void* GetFirstUCharacterHashTable(char** idsoData, int idsoDataSize, int hashBase)
{
        // 当hash表基数小于成语数组长度时,返回空指针
        if(hashBase < idsoDataSize)
        {
                // return NULL;
        }

        // 动态分配首字索引hash表内存
        int (*firstUcHashTable)[2] = (int(*)[2])calloc(hashBase * 2, sizeof(int));

        // 循环遍历每个成语,构建首字索引hash表
        int i;
        for(i = 1; i < idsoDataSize; i++)
        {
                // 计算首字hash值
                int firstUcHashIndex = BKDRHash((char*)idsoData[i], FIRST_UC, hashBase);

                // 构建首字索引hash表
                if(firstUcHashTable[firstUcHashIndex][0] == 0)
                {
                        firstUcHashTable[firstUcHashIndex][0] = i;
                        firstUcHashTable[firstUcHashIndex][1] ++;
                        continue;
                }

                // 获取UTF汉字字节长度
                int ucLen = GetUcLen((unsigned char)(*(idsoData[i])));

                // 比对成语首字是否重复
                if(strncmp((char*)idsoData[i], (char*)idsoData[firstUcHashTable[firstUcHashIndex][0]], ucLen) == 0)
                {
                        // 首字成语 组计数器
                        firstUcHashTable[firstUcHashIndex][1] ++;
                }
                else
                {
                        // 构建首字索引失败,释放hash表内存
                        free(firstUcHashTable);

                        // 返回空指针
                        return NULL;
                }
        }

        // 返回首字索引hash表
        return (void*)firstUcHashTable;
}

// 获取成语全字hash桶
void* GetWholeUCharacterHashTable(char** idsoData, int idsoDataSize, int hashBase)
{
        // 当hash表基数小于成语数组长度时,返回空指针
        if(hashBase < idsoDataSize)
        {
                // return NULL;
        }

        // 计算需要的hash桶数量
        int bucketNumber = (hashBase / BUCKET_DEEP_SIZE) * (64 / BUCKET_DEEP_SIZE);

        // 动态分配hash桶内存
        int (*wholeUcHashTable)[BUCKET_DEEP_SIZE] = (int(*)[BUCKET_DEEP_SIZE])calloc(bucketNumber * BUCKET_DEEP_SIZE, sizeof(int));

        // 循环遍历每个成语,构建全字hash桶
        int i;
        for(i = 1; i< idsoDataSize; i++)
        {
                // 计算全字hash值
                int wholeUcHashIndex = BKDRHash((char*)idsoData[i], WHOLE_UC, bucketNumber);

                // 循环桶深度
                int j;
                for(j = 0; ; j++)
                {
                        // 超过桶深度、则返回空指针
                        if(j >= BUCKET_DEEP_SIZE)
                        {
                                // 构建hash桶失败,释放hash桶内存
                                free(wholeUcHashTable);
                                return NULL;
                        }

                        // 构建hash桶
                        if(wholeUcHashTable[wholeUcHashIndex][j] == 0)
                        {
                                wholeUcHashTable[wholeUcHashIndex][j] = i;
                                break;
                        }
                }
        }

        // 返回首字索引hash表
        return (void*)wholeUcHashTable;
}

// 帮助信息
int GetHelpInformation()
{
        fprintf
        (
            stdout
            ,
            "[欢迎你使用 \"成语接龙\"] --- 输入Q退出\n"
            "[小娜]: 我是萌萌的小娜!\n"
        );
        return 0;
}

// 成语接龙SHELL
RetIdso IdsoShellCore(int firstUcHashBase, int bucketNumber, int (*firstUcHashTable)[2], int (*wholeUcHashTable)[BUCKET_DEEP_SIZE])
{
        // 随机一个待接成语
        char* needUcStr = (char*)idso_data[rand()% (idso_data_size-1) + 1];

        // 计算待接成语末字hash
        int needUcStrLastUcHash = BKDRHash(needUcStr, LAST_UC, firstUcHashBase);

        // 如果待接成语不可接,则退出SHELL,休眠随机种子以待下次随机
        if((firstUcHashTable[needUcStrLastUcHash][0] == 0) || (firstUcHashTable[needUcStrLastUcHash][1] <= IDSO_SHELL_DIFFICULTY))
        {
                return SHELL_USER_RAND;
        }

        // 接受输入字符串的容器
        char inPut[INPUT_BUFF_SIZE + 1];

        // 接受输入字符串转输入数组的容器
        char* sArgv[3];
        int sArgc;

        // 接龙循环
        while(true)
        {
                // 显示人机交互信息
                fprintf
                (
                        stdout
                        ,
                        "[小娜]: %s\n"
                        "[>>>>]: "
                        ,
                        needUcStr
                );

                // 接收用户输入流
                fgets(inPut, INPUT_BUFF_SIZE, stdin);

                // 找到第一个换行符的位置
                char* p =strchr(inPut, '\n');

                // 如果输入为空,则显示帮助信息
                if(p == inPut)
                {
                        GetHelpInformation();
                        continue;
                }

                // 置输入流结束符
                if(p != NULL)
                {
                        *p='\0';
                }

                // 使用按键 Q退出
                if(! strcasecmp(inPut, "Q"))
                {
                        fprintf(stdout, "[感谢使用,再见!]");
                        return SHELL_USER_EXIT;
                }

                // 切分输入字符流
                sArgc = 0;
                p = inPut;
                while(*p)
                {
                        while(*p == ' '|| *p == '\t')
                        {
                                *(p ++) = 0;
                        }

                        if(*p)
                        {
                                if(++ sArgc > 3)
                                {
                                        break;
                                }
                                sArgv[sArgc - 1] = p;
                        }

                        while(*p && (*p != ' ') && (*p != '\t'))
                        {
                                p += GetUcLen((unsigned char)*p);
                        }
                }

                // 执行成语接龙核心
                if(sArgc == 1)
                {
                        bool isCorrect = false;

                        // 计算用户输入全字hash值
                        int inPutWholeUcHash = BKDRHash(sArgv[0], WHOLE_UC, bucketNumber);

                        // 遍历全字hash桶
                        int j;
                        for(j=0; (j<BUCKET_DEEP_SIZE) && (wholeUcHashTable[inPutWholeUcHash][j] != 0); j++)
                        {
                                char* pUcStr = (char*)idso_data[wholeUcHashTable[inPutWholeUcHash][j]];

                                // 比对桶中字符串
                                if(! strcmp(sArgv[0], (char*)pUcStr))
                                {       
                                        // 计算用户输入 首字hash
                                        int inPutFirstUcHash = BKDRHash((char*)pUcStr, FIRST_UC, firstUcHashBase);

                                        // 比对首字hash,判断首字是否可接龙
                                        if(inPutFirstUcHash != needUcStrLastUcHash)
                                        {
                                                // 用户首字没有接上、退出循环
                                                break;
                                        }

                                        // 计算末字hash索引
                                        int inPutLastUcHash = BKDRHash((char*)pUcStr, LAST_UC, firstUcHashBase);

                                        // 依据末字hash 检索以其打头的成语
                                        if(firstUcHashTable[inPutLastUcHash][0] != 0)
                                        {
                                                // 机器随机挑选一个 以其字打头的成语
                                                int randIndex = rand() % firstUcHashTable[inPutLastUcHash][1];
                                                char* rUcStr = (char*)idso_data[firstUcHashTable[inPutLastUcHash][0] + randIndex];

                                                // 计算此随机成语末字hash
                                                int rUcStrLastUcHash = BKDRHash(rUcStr, LAST_UC, firstUcHashBase);

                                                // 判断此随机成语,若末字不可接
                                                if(firstUcHashTable[rUcStrLastUcHash][0] == 0)
                                                {
                                                        // 显示绝杀成语
                                                        fprintf(stdout, "[绝杀]: %s\n", rUcStr);

                                                        fprintf(stdout, "[你输了]\n");
                                                        return SHELL_USER_FAILED;
                                                }

                                                // 末字可接,更新待接成语
                                                needUcStr = rUcStr;

                                                // 末字可接,更新待接首字hash
                                                needUcStrLastUcHash = rUcStrLastUcHash;
                                        }
                                        else
                                        {
                                                // 机器无法接龙用户输入的成语,用户获胜
                                                return SHELL_USER_SUCCEED;
                                        }

                                        // 回答正确,则标记为真
                                        isCorrect = true;
                                        break;
                                }
                        }

                        // 用户回答错误
                        if(! isCorrect)
                        {
                                // 计算可接成语数目
                                int pickUpCount = firstUcHashTable[needUcStrLastUcHash][1];
                               
                                // 当接龙难度过高时,机器给予用户提示
                                if(pickUpCount <= IDSO_SHELL_DIFFICULTY)
                                {
                                        // 机器随机一个答案
                                        char* promptUcStr = (char*)idso_data[firstUcHashTable[needUcStrLastUcHash][0] + rand() % pickUpCount];
                                        fprintf(stdout, "[小娜悄悄提示 \"%s\"]\n", promptUcStr);
                                }
                                else
                                {
                                        fprintf(stdout, "[小娜希望你能认真考虑!]\n");
                                }
                        }
                }
                else
                {
                        // 帮助信息
                        GetHelpInformation();
                }
        }
}

// 主函数
int main(int argc, char *argv[])
{
        // 使用混合快排,先对成语数组进行前4字节索引排序
        UcsQsort((char**)idso_data, 1, idso_data_size-1);

        int firstUcHashBase = 25511, wholeUcHashBase = 18311;
        void *pFc = NULL, *pHc = NULL;

        // 建立首字索引hash表
        while((pFc = GetFirstUCharacterHashTable((char**)idso_data, idso_data_size, (++ firstUcHashBase))) == NULL);
        int (*firstUcHashTable)[2] = (int(*)[2])pFc;

        // 建立全字hash桶
        while((pHc = GetWholeUCharacterHashTable((char**)idso_data, idso_data_size, (++ wholeUcHashBase))) == NULL);
        int (*wholeUcHashTable)[BUCKET_DEEP_SIZE] = (int(*)[BUCKET_DEEP_SIZE])pHc;


        // 计算hash桶基数
        int bucketNumber = (wholeUcHashBase / BUCKET_DEEP_SIZE) * (64 / BUCKET_DEEP_SIZE);

        // 初始化随机种子
        srand((unsigned)time(NULL));

        // 打印SHELL题头
        GetHelpInformation();
       
        // 调用成语接龙SHELL核心
        while(true)
        {
                // 获取接口SHELL返回值
                RetIdso retIdso = IdsoShellCore(firstUcHashBase, bucketNumber, firstUcHashTable, wholeUcHashTable);

                // 检测是否退出接龙SHELL
                if(retIdso == SHELL_USER_EXIT)
                {
                        // 退出接龙SHELL
                        break;
                }
                else if(retIdso == SHELL_USER_SUCCEED)
                {
                        // 用户获胜                                                fprintf(stdout, "[你赢了]\n");
                        fprintf
                        (
                            stdout
                            ,
                            "[欢迎你使用 \"成语接龙\"] --- 输入Q退出\n"
                            "[小娜]: 我是萌萌的小娜!\n"
                        );
                }
                else if(retIdso == SHELL_USER_FAILED)
                {
                        // 用户战败
                        fprintf
                        (
                            stdout
                            ,
                            "[欢迎你使用 \"成语接龙\"] --- 输入Q退出\n"
                            "[小娜]: 我是萌萌的小娜!\n"
                        );
                }
                else if(retIdso == SHELL_USER_RAND)
                {
                        // 延缓时间,提高rand()成语随机性
                        IDSO_SLEEP(1);
                }
        }

        // 释放内存
        free(firstUcHashTable);
        free(wholeUcHashTable);
        return 0;
}
[/code]

CrLf 发表于 2017-11-12 03:55

建议楼主打包一个小样本,或者在 m 选项中提供 10 行左右的示例
而且以下诸项究竟有什么作用也语焉不详
BYTE LOW:         0
BYTE HIGH:        255
INSERT MIN:       1
INSERT MAX:       8
STEP FORWARD MIN: 1
STEP FORWARD MAX: 2
对很多工具来说,文档其实和代码一样重要——除非是自己写着玩

CrLf 发表于 2017-11-12 03:57

为什么出来的结果有一大堆的 (null),这部分对使用者来说毫无意义嘛...

happy886rr 发表于 2017-11-12 09:22

[i=s] 本帖最后由 happy886rr 于 2017-11-12 14:34 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=204226&ptid=45901]3#[/url] [i]CrLf[/i] [/b]
很抱歉大师,我的文档和压缩包都copy /b到图片里了,你没下载图片存a.zip吗?(外链图有效期仅7天,过期不再提供任何下载,现在图片已经失效。不过我已投递副本到你邮箱请注意查收)
那里边有详细的使用说明,和每个参数的含义,并附带多本统计词典。

压缩包里有个readme.txt,里边已经写明如下信息:[code]关于.DI格式,是一种全新研发的字典表统计格式
/|||
TRIE - DICTIONARY DATABASE 1.0
==============================================================
DICTIONARY NAME:  ENGLISH DICTIONARY TABLE     //字典名
MADE DATE:        2017-10-25                   //字典创建时间
CHARSET:          UTF8                         //字典编码
USE ENGLISH MODE: 1                            //是否英文模式
USE SUBMATCH MODE:0                            //是否前缀模式
MAX SHOW:                                      //最大显示数目
==============================================================
BYTE LOW:         65                           //字符的最小值
BYTE HIGH:        122                          //字符的最大值
INSERT MIN:       1                            //串长的最小值
INSERT MAX:       16                           //串长的最大值
STEP FORWARD MIN: 1                            //粒度的最小值
STEP FORWARD MAX: 1                            //粒度的最大值
==============================================================
|||/[/code]

CrLf 发表于 2017-11-12 13:18

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=204227&ptid=45901]4#[/url] [i]happy886rr[/i] [/b]


    感谢分享,这下会用了

失控的疯子 发表于 2019-1-25 15:49

感谢分享,这下会用了

CrLf 发表于 2019-8-1 00:12

附一个下载地址:
[url]http://www.bathome.net/s/tool/index.html?key=trie[/url]

页: [1]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.