前言
数据结构专栏,经典数据结构的分析和应用
探讨输入法的实现,以及五笔输入法浅析
引入
笔者针对字库在另一个专栏有细致的说明,看不看都不影响这一帖.原因是那部分内容更底层,如果没兴趣会很枯燥,而且多数程序员的工作不会涉及底层(难度大,需要的人少),有现成开源的东西可以用,也没理由拒绝.
索引的抽象
字库是由"多重索引表(索引文件)+数据文件"组成的(笔者基于拼音输入法用了三张索引表加一张索引总表,和若干数据文件构建字库).这个公式很重要,可以解决一切散列结构的问题.往大一点说,解决所有数据类型的问题,再放大一点,所有程序问题都可以解决(限数据部分,不含算法).
这里给出一张图说明如何从索引到数据.以下输入索引的前两位:c和b

1>序号不是必须的,可以用可以不用.序号也可以用作索引值,此时输入数字1,2等
2>这里的++第一张索引表++ 对应的是前面帖子中的"++第三张索引表++",是第一个索引进来搜索的表
3>并非只有两张索引表,可以有多张,最后递进到数据文件(.dat)---图中并未表现
4>两张索引表存在的对应关系:后面的索引表(第二张)"缩"引到前面索引表的一项.但他们的关系不一定是一一对应的,(这里的内容有一点难,简单理解他们是一一对应的)
索引的作用
回到正题,索引的作用到底是什么?在++数据文件++ 和++索引表++存在的前提下,索引=数据.
打个比方:你要到快递驿站去取快递,你的快递放在第4号驿站,第3个货架,第2排,第1个包裹的位置.那么4321就是你的包裹的代码.把包裹换成数据,把代号换成索引,就和这里对应上了.
输入法的实现
如前所述,字库采用"索引编码+索引表集合+数据文件"的架构,给出的接口是这样的(假设):
|----|------|------|
| 序号 | 编码 | 对应汉字 |
| 1 | 4321 | 集 |
| 2 | 8765 | 合 |
如果自己开发一个全新的输入法(假设以拼音为索引编码),那么请问表示汉字的那些像素点是你去数还是我去数?显然这是巨大且没有什么技术含量的工作,并没有这个必要,因为有人已经做过这些事了.所以除了最初的字库以外,其他的输入法都属于"套壳"的作品.
用散列表写个外框,在数据出现的地方,加上表示汉字的编码,一个输入法就出现了.
=============================内容分割线↓===================================
那么笔者前面所有的内容都没有意义吗?见仁见智.至少懂得他的架构,加深对底层的理解:都是些数据,0和1,都是些地址,这些东西组成了庞大的计算机世界.机器指令这部分虽没有涉及,但他们也是0和1,只是解析方法不一样.
=============================内容分割线↑===================================
五笔输入法浅析
笔者没学过五笔输入法,但他的底层架构和笔者几篇帖子中分析的应该差不多.
拼音输入法的劣势
回头再分析为什么用拼音做索引并不是好方案.按照之前的设计,需要的内容如下:
1>一张索引总表,记录索引表的地址和数据文件(每个声母表达的所有汉字)的地址
2>三层索引表.除去i,u,v这三个声母,需要23个声母共69个索引文件
3>数据文件,同样需要23个表示每声母汉字的数据文件
加起来需要93个文件.能不能把一些内容合起来呢?可以的.但从另一个角度出发,满足"词语"输入这个需求的话,单独列出来更好操作.可以随时适应变化(词语输入的实现笔者以后可能会讲一讲).数据文件基本上是不会有多少变化的,庞大的索引文件会影响性能,所以拼音做索引不是一个好的方案---特别是在早期计算机资源(CPU和内存)有限的情况下(现在当然不存在这个问题,偏向于功能性和适用性,这也是为什么拼音输入法最终会被大多数人接受的原因---学习成本小)
五笔的索引体系分析
五笔输入法中的汉字,由若干字根组成.他们分布在键盘的若干个按键上.所以索引总表如下:
|----|------|-----|-----|-----|-----|
| 序号 | 键盘字母 | 字根1 | 字根2 | 字根3 | 字根4 |
| 1 | q | 亻 | 火 | 贝 | ... |
| 2 | r | 弋 | 戈 | 扌 | ... |
| 3 | ... | ... | ... | ... | ... |
| | | | | | |
| 索引总表 ||||||
他的表达可以看作:所有汉字(字库)由字根集合(键盘字母集合)组成.因为和其他层次的索引没有一对多的关系,所以称作"索引总表"比较合适.
第一层索引
每个键盘字母,第一层索引表示:所有以该字母开头的字根包含的所有汉字.他由其成字的笔数分为一笔字,二笔字,三笔字,四笔字,五笔字.表达如下:
|----|-----|----|
| 序号 | 项目 | 个数 |
| 1 | 一笔字 | 1 |
| 2 | 二笔字 | n |
| 3 | 三笔字 | m |
| 4 | 四笔字 | o |
| 5 | 五笔字 | e |
| | | |
| 键盘字母q第一层字根索引(非实际)表 |||
| 键盘字母q第一层字根索引(非实际)表 |||
1>按照五笔的说法,最多五笔成字,所以最大是五笔字
2>个数指当前字母开头的字根,和其他字母能成字的组合个数.一笔字最大是1,表示靠单个字根能成字,否则是0.假设所有字根由10个键盘字母组成,而当前字母均可以与之成字(包括自己),则二笔字个数是10.
第二层索引
简单描述如下:
|-----|--------|------|
| 序号 | 对应键盘字母 | 汉字个数 |
| 1 | q | 3 |
| 2 | qq | 3 |
| 3 | qr | 2 |
| 4 | qrq | 1 |
| ... | ... | ... |
| | | |
| 键盘字母q第二层字根索引(非实际)表 |||
| 键盘字母q第二层字根索引(非实际)表 |||
第二层索引根据假设的索引总表内容而来.
q对应的汉字:人,火,贝
qq对应的汉字:从,炎,伙
qr对应的汉字:代,伐
qrq对应的汉字:贷
说明:第二层索引文件的设计和实际情况有些差别
第三层索引文件和数据文件不再赘述
五笔的优点
五笔采用一种"模糊"索引的构建方法,减少了索引文件的数量.
汉字的存储
索引存储
尽管前面已描述汉字的存储方法(指针),但笔者仍想从头梳理一遍思路.
首先,索引+算法+字库=具体汉字.操作方法(以拼音索引为例),要得到"人"这个字,输入"ren"
那么,汉字的存储采用索引是否可行呢?显然是可以的.以上面为例,ren占3个字节.将此方法推广到所有汉字,每个汉字存储从a到zhuang(卷舌音,介音,后鼻韵都加上),每个汉字占1到6字节
最精简的存储方案
把所有汉字放进一个数组内,假设有10000个汉字,用指针标识,则2的14次方=16384,则每个汉字用14位.但汉字字库并不是使用数组而是用散列表实现的,因此仅作探讨.
多重索引的存储方案
每层索引表达的最大数字,组合起来,即得到准确的存储大小.
以拼音索引为例,索引总表有23项(所有字母除去u,i,v),则用5位存储(2的5次方等于32),在五笔输入法的第一层索引里,确定的是5项,所以用3位存储.
GBK和UTF-8
按照标准化的汉字存储,GBK占3个字节24位,UTF-8占2个字节16位,其内在思路同上.
举例
假设有一个文件,里面的汉字是:我从山中来,带着兰花草.拼音:wocongshanzhonglai+逗号+daizhelanhuacao,++假设逗号算一个字节++
如果用拼音索引存储,加1个逗号,共34字节.
如果是GBK编码,十个汉字30字节加1个逗号,31字节.
如果是UTF-8编码,十个汉字20字节加1个逗号,21字节.
索引存储占用空间最大,并不是理想的方案.
标点符号的存储
如果有多种输入法(中文,英文,日文,韩文等),标点符号可以作为单独的内容,加入每种输入法中,以适应统一的编码存储.
汉字文件的具象
假设汉字文件里的每个字符按2个字节,则机器里存储的文件大概长这个样子:
0x1234,0x2ab0,.....0x38f2....末尾符号. //2个字节一个单位,末尾不能是奇数
五笔字根的键盘分布
回头再看五笔输入法的基础部分的问题:如何将字根合理分配到键盘上.
字根多少基本上是固定的,由汉字的样子决定.这里要解决一个数学问题:有n个(n的大小是固定的)字根,分布到m个按键上,键盘的使用尽量均衡.
笔者也不是数学专业,可以想到的是一种基本解法:把所有的汉字的字根拆开,做一个统计,如下所示:
|----|----|-----|-----|---|----|----|----|
| 序号 | 汉字 | 字根1 | 字根2 | | 序号 | 字根 | 计数 |
| 1 | 代 | 亻 | 弋 | | 1 | 亻 | 3 |
| 2 | 人 | 亻 | | | 2 | 弋 | 1 |
| 3 | 伙 | 亻 | 火 | | 3 | 火 | 1 |
| | | | | | | | |
| 汉字分拆表 |||| | 字根统计表 |||
1>两张表是配套的,最后得到每个字根的计数值a.然后计算出所有字根的计数总值s.
2>按键的个数自己设置m,计数总值s/按键个数m=每个按键上分配的计数值t.
3>根据t,使得若干个字根的计数值a加起来接近于t.解出来的结果可能有多个.在此结果的基础上,使每个按键的字根数接近.
由此再次证明了编程解决的最终都是数学问题,代码只是表达方式
题外话:四笔或者六笔?
为什么是五笔输入?因为绝大多数的汉字都可以用字根在五笔以内完成.如果有复杂结构超过五笔的,也可以像标点符号一样单独列出来作为索引项插入.
小结
输入法以及五笔输入法的一些分析
最应该感谢的是开源创作的前辈,有了开源的内容支持,才能写出更多功能的程序.