一个非常小巧高效的pinyin输入法

发布于 2021-01-08 21:19:30

pinyin_ime.jpg
找了许多输入法,都是很大很慢,就自己撸了个,效率快多了。
这是第一版,缓冲了字库数据,缺点是效率低了点,优点是方便以后扩展新功能
pinyin.c
pinyin.h
这是简化的版本,由于撸掉了strlen等各种计算,有极高的检索效率
pinyin.c
pinyin.h
第一个版本基本走的商业输入法的路子,各种缓冲一堆堆,然并无卵用,如前说述,是扩展更多新功能必须的做法,例如多拼音待选,根据检索频度调整词汇位置,录入新词汇等.
第二个版本则是纯考虑嵌入式的特点,废弃字符串长度计算,检查等操作,缺点是字词库必须符合要求的个数.两者各擅胜场,无所谓那个更好.
代码很简单,输入法实现为一个独立的模块,可以方便的集成到需要的地方,用串口都能控制它工作。

使用方法

第一步,定义一个input_method_t类型的变量。
例如。

static input_method_t myime;

第二步,初始化myime。

pinyin_init(&myime);

第三步,调用以下接口录入一个字母;c只接受a-z,A-Z。

pinyin_getin_letter(&myime, c);

例如。

pinyin_getin_letter(&myime, 'a');

执行这个函数后,与字母a有关的汉字已经拷贝到myime下。
inbuf中存储的是输入的字母a。
wordstr存储的是当前的一个页,显示内容如下。

1.安 2.按 3.爱 4.阿 5.暗 6.啊 7.挨 8.碍 9.凹

如果有GUI系统,就可以用label控件显示这两条内容。
如果字比较多,会有多个page。
page_numbs表示有几个page。
page_index表示当前的是哪个page。
用户可以直接更改page_index的值实现翻页,再调用

pinyin_generate_page(&myime);

实现页面内容更新。

左翻页示例代码。

if (ime->page_numbs > 1)
{
    if (ime->page_index > 0)
    ime->page_index -= 1;
    memset(ime->wordstr, 0, IME_WORDSTR_SZ);
    pinyin_generate_page(ime);
    //此处可显示wordstr中的内容到GUI
}

右翻页示例代码。

if (ime->page_numbs > 1)
{
    if ((ime->page_index + 1) < ime->page_numbs)
        ime->page_index += 1;
    memset(ime->wordstr, 0, IME_WORDSTR_SZ);
    pinyin_generate_page(ime);
    //此处可显示wordstr中的内容到GUI
}

如果需要取消一个输入的字母,调用函数。

pinyin_backspace(&myime);

当需要选中一个字或词时,示例代码。

unsigned short zh[16] = {0};
pinyin_getout_zhstring(&myime, zh, 4);

以上面的wordstr内容为例进行说明。

1.安 2.按 3.爱 4.阿 5.暗 6.啊 7.挨 8.碍 9.凹

zh[]中会返回"暗",因为是编号从0开始的。

如果想方便的处理page中的每个汉字或词语,也可以直接使
用数组myime.s,数组s中保存了page中的字或词。

s[0]安 s[1]按 s[2]爱 s[3]阿 s[4]暗 s[5]啊 s[6]挨 s[7]碍 s[8]凹

结构体成员说明

结构体struct input_method说明。
type;//表示检索的是字典还是词典。
*dicts;//当前字典。
*terms;//当前词典。
inbuf;//保存了输入的ascii字符串。
zhbuf;//保存了当前字典或词典下的所有内容。
wordcnt;//反映了字/词典中每个字词的长度。
        //如果检索的是字典wordcnt=1。
        //如果检索的是词典wordcnt=struct rtgui_terms下的cnt。
inlen;//保存了输入的ascii字符串长度。
zhlen;//保存了检索到的字/词的个数。
cnt_per_page;//保存了一个page中可以显示多少个词。
cnt_cur_page;//保存了当前page中有多少个词。
page_index;//记录当前page编号。
page_numbs;//记录总的page数量。
wordstr;//由当前page生成的带编号的待选词字符串,用于GUI显示。
s[9][20]; //对应wordstr的内容,为了方便处理字/词。

====================我是一条华而不实分隔线=======================
设计这个pinyin输入法的初衷是任何地方都能用,所以它是一个独立的模块,
设计目标是一定要快速反应,即使跑在8051上,应该也能流畅.
所以综合参考了许多输入法的实现,就定稿成目前的版本,称之为V0.1吧.

下面分析一下设计思路.

由于需要它能尽可能的快,例如键入一个字母'a'后,能够最短时间内就能反馈回备选词
列表,这个特点在低速设备上会非常受益.所以对字典/词典根据字母进行分类.这样在
浏览字典时就大大缩小了范围,避免做更多无用功.

有的输入法是字典的每条记录的一个
拼音只对应一个汉字或词语,且都记录在一个字典列表中;以GB2312编码6700多个和
GBK编码21000多个的体量,这个检索工作是惊人的.所以坚决使用字母分类字典,现在是
一级分类,如果词语量非常大,可以二级分类,即用第一个字母确定字母字典,第二个字母
确定子分类字典.这样可以将检索范围进一步缩小,以提高检索速度.

字典的第二个制定原则是,一个pinyin对应多个同样发音的字词.用户键入拼音后往往都
会在待选列表中观察一会再选择.如果首页未命中,需要翻页.那么这些待选词当然是在一
个线性表中更方便和快速的处理,否则还要从字典中检索出所有同音的建立线性表,导致
处理时间不确定,结果是字典比较小的顺畅,字典大的卡顿.使用同发音字词集中在一起的
原则后,处理的代码简化了,效率也高.
同音字词放在一个线性表中,也可以方便加入热频词功能,可以根据字词的使用频率排序;
该原理比较简单,这个版本暂时不加.

第三个原则是,不使用文件系统存放字词库.如果使用了文件系统,拼音输入法的应用
范围也比较受限(没文件系统不让玩),一是文件系统的访问速度瓶颈,一是需要额外RAM
中建立字库cache.商业输入法都是这么做的,这里主要面向嵌入式,牺牲点ROM换取高效率,
是合适的.

核心代码寥寥几行,看源文件吧,上面已经将源代码干的事说明白了.

查看更多

关注者
1
被浏览
597
HappyTime
HappyTime 2021-01-09

厉害,大佬可以搞成软件包啊

10 个回答
小住住
小住住 认证专家 2021-01-08

这个厉害,输入法都可以。

sync
sync 2021-01-09

先收藏了,楼主威武!

pkokoc
pkokoc 2021-01-09

牛B,加到软件包里去

bernard
bernard 2021-01-10

👍大佬

iamyhw
iamyhw 2021-01-10

初步计算后,感觉不需要将字库的数据缓存起来,原因是,缓存前检查一遍数据中是否有空格,然后再复制到缓存中。这个步骤相当于读了两遍数据,以后生成page数据又读一遍。太丧了。应该只一遍就到生成page。嗯,参考别人的坏处就是思路被带着走了。如果字典词典数据格式是完全正确的,的确不需要检查空格啥的。可以提供一个专门工具专门检查字典词典,代码中取消检查,这样才是最快的!
pinyin.c
pinyin.h

static const char **dicts_second[26] =
{   "ino","aeiou","aehiou","aeiou","inr","aeou","aeou","aeou",NULL,"iu","aeou","aeiouv","aeiou",    "aeiouv","u","aeiou","iu","aeiou","aehiou","aeiou",NULL,NULL,"aeou","iu","aeiou","aehiou"
};

与首楼第二版的区别,
在pinyin_update_zhbuf()开始处加入这个,快速剔除非完整拼音直接去查找词库,

if (ime->inlen >= 2)
    {   //剔除无效拼音,只判断前两个字母,再多就与全检索一遍的效率差不多了
        if (!strchr(dicts_second[ime->inbuf[0]-'a'], ime->inbuf[1]))
            goto findterms;
    }

查找词库,首先匹配前面的字符串是否有正确的拼音,如果该拼音与词汇同时有效,优先选择词汇,同时完整拼音高亮,等待用户选字,这个逻辑就有点复杂了,词库的规模如果非常大,这个的检索效率会是一个问题.
现在对反应时间比较满意了,剩余的就是完善词库.词库尽量因需求而定,别搞的太大,建议仅包含些行业相关的就行了.目前人类的词库不知道有多少个T的容量,是一个海量.

检索过程有3种情况,
-直接从字库种找到
-再次从词库种找到
-最差的是根据输入的拼音,从字库种匹配到一个最长的
所以词库的编写需要根据检索算法有针对性的定制,
字库只能匹配到完整的拼音,所以词库的拼音前面最好有完整的拼音存在,因为按照上面的原则,最差的情况,可以匹配到一个合适的字库来显示.
例如:
如果有个词库{"zhangf","丈夫,涨幅"}
现在键入了zhangfe,最差可以显示zhang的词库,
如果键入的是zf,如果没有该词条,那就没有任何东西可以显示.

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览