数据结构_字符和汉字的编码与查找

前言

数据结构专栏,经典数据结构的分析和应用,同时关联贫道的收费专栏--数据类型设计

引入

探讨汉字和标准键盘字符在底层的存储和查找,既是独立内容,又和笔者另一篇贴数据类型设计_散列表经典应用_字典有关联,方便读者自行选择.

字符(汉字)在硬件层面的表示

如笔者前面帖子所述,字符(汉字)本质上是一张无规则点状图,如下图(蓝线部分表示汉字"二")

上图:汉字"二",表示为{1,1},{1,2},{3,0},{3,1},{3,2},{3,3}.一个字是n个二维点坐标.char zifu[n][2].

如果是C++表示如下:

复制代码
//字符类型
struct zifu{
    vector<Point> vp;   //点集合
};

//点类型,无颜色
struct Point{
    char x_cord;        //x坐标
    char y_cord;        //y坐标
}

有两个隐含的假设条件:

1.x坐标和y坐标的数据类型char,限定255.无论那个字符,横向和纵向最多256个点

2.点是没有颜色的,如果有颜色,数据类型Point有差别.参考笔者另一个贴:数据类设计_图片类设计之8_自由图形类设计_(前端架构)

两点说明:

1.这里的二维坐标点采用默认大小(假定字号8),也就是说每个字符对应的二维点坐标是固定且不同于其他字符的.暂时不考虑字体缩放带来的影响.

2.二维点如何表示成汉字?每个汉字将在屏幕上占用一个方块位置,基点对应上图中行0,列0的位置.每输入一个汉字,把这些二维点集合从字库中提取出来,写入这个方块中.像田字格一样,如图

---------来源于某度,左上角那个点对应基点,外框相当于屏幕方块,坐标系向右向下为正

字符(汉字)的存储

上面分析了字符的表示,接下来看字符是如何存储的.

首先,存储以文件格式进行.那么,一个文件存一个字符吗?在<深入理解计算机系统>P632提到文件存在元数据(每生成一个文件都将有额外的数据产生).而上面的汉字"二"自身占据12个字节(可能还没有元数据多),显然一个文件存一个字符是不经济的.

那么,很自然想到用一个文件存一个音节的字符(注意:这里仍然是以拼音输入法为参照,实际可能未必如此,将在后面说明).字符属于复杂数据 (复杂数据在笔者另一篇贴:数据类型设计_数据的概念的"高级语言中数据分类"有提及).当多个复杂数据放入一个文件中时,需要一张索引表来标识每个数据的长度.举例:假设音节er有三个汉字"二","而"和"尔",放入了同一个文件er.dat中,那么和er.dat同时存在有一个索引表文件(设为er.index),他的内容如下:

|----|------|------|
| 序号 | 对应汉字 | 所占空间 |
| 1 | 二 | 12 |
| 2 | 而 | 40 |
| 3 | 尔 | 30 |
| 4 | ... | ... |
| er.index(部分) |||

注意:中间项"对应汉字"不需要表现出来,只作为理解使用.查找到索引表之后,根据对应汉字所在位置,计算出指针偏移,然后获得这个字符.

解释:每个复杂数据的访问有三点要准确:指针指向首地址要正确,指针偏移量要准确---复杂数据中包含每个简单数据(字长单元及以下长度的数据)的长度,复杂数据的总长度要准确---保证指针指向下一个复杂数据的准确位置.

索引表的存储

索引表非常重要,索引表+数据文件=数据.输入搜索条件,根据索引表和数据文件,即找到数据

索引表和字符一样,考虑怎样存储.索引表是复杂数据,其数据类型为

复制代码
struct Index{
    vector<short> vs;
}

short类型的由来:前面假设字符的长度和宽度方向点最大为255,则所占空间最大是65535.因此在索引表中每有1个字符,占用2个字节空间.前面假设音节er有三个汉字,占用6字节空间,也是很小的.所以考虑将所有e开头的索引表放入同一个文件(假设为e.index)中.

假设e开头的音节有ei,en,er这三个.建立一个e.index把3个音节的索引表放进去,如图

|----|------|------|----|------|------|----|------|------|
| 序号 | 对应汉字 | 所占空间 | 序号 | 对应汉字 | 所占空间 | 序号 | 对应汉字 | 所占空间 |
| 1 | 欸 | 80 | 1 | 嗯 | 80 | 1 | 二 | 12 |
| 2 | 诶 | 60 | 2 | 恩 | 40 | 2 | 而 | 40 |
| 3 | 誒 | 100 | 3 | 摁 | 60 | 3 | 尔 | 30 |
| ei.index内容 ||| en.index内容 ||| er.index内容 |||
| | | | | | | | | |
| e.index(表) |||||||||

为方便描述,e开头音节包括3个(实际可能不止),每个音节包含的汉字设定为3个(忽略实际).注意:e.index是索引文件,包含了ei.index,en.index及er.index,符合上一小节的红色描述部分.所以e.index需要一个索引表.设计如下:

|----|----|----------|------|------|------|
| 序号 | 音节 | 地址 | 汉字个数 | 指针偏移 | (说明) |
| 1 | ei | ei.dat地址 | 3 | 0 |   |
| 2 | en | en.dat地址 | 3 | 6 |   |
| 3 | er | er.dat地址 | 3 | 12 | |
| | | | | | |
| e.index索引(部分) ||||||

说明:

1>++音节++这一项用于输入字符的检索,比如输入ei,再用指针指向ei.dat(未描述),去里面找数据.

2>++汉字个数++ 和++指针偏移++ 始终是2倍关系,这里使用指针偏移,表示从e.index表开始查找

3>上表中的++地址++项,假设为内存中的绝对地址,操作系统加载后即可使用,无需从硬盘传入数据(简化理解)

第二层索引表

与此同时,e.index的索引部分(上表)也需要做存储,遇到前面同样的问题.解决方法相同,和其他的索引表合成一个文件,假设叫做pinyin.index,设计如下:

|----|-----|----------|------|----|-----|----------|------|
| 序号 | 音节 | 地址 | 指针偏移 | 序号 | 音节 | 地址 | 指针偏移 |
| 1 | a | a.dat地址 | 0 | 5 | ei | ei.dat地址 | 0 |
| 2 | ai | ai.dat地址 | n | 6 | en | en.dat地址 | 6 |
| 3 | an | an.dat地址 | p | 7 | er | er.dat地址 | 12 |
| 4 | ... | ... | ... | 8 | ... | ... | ... |
| | | | | | | | |
| pinyin.index(表) ||||||||

第三层索引表

再用一张声母表(shengmu.index)来检索每张索引表的地址,如图

|----|----|-----------|-----|-----|-----------|
| 序号 | 声母 | 地址 | 序号 | 声母 | 地址 |
| 1 | a | a.index地址 | 5 | e | e.index地址 |
| 2 | b | b.index地址 | ... | ... | ... |
| 3 | c | c.index地址 | ... | ... | ... |
| 4 | d | d.index地址 | 26 | z | z.index地址 |
| | | | | | |
| shengmu.index(表) ||||||

说明:设计时有些内容没有太重视,例如e.index,e开头的有额,鹅等汉字没表达.还有声母表不需要26项,如u,i开头的没必要做在表中,方便理解忽略掉部分细节.

至此汉字库的结构完毕,上述后缀为(表)的留下,总共有3张表,加若干数据文件(.dat)

算法

增加删除略,主要看如何查找,即输入音节的同时如何找到汉字

当输入一个音节,如en时,现在第三层索引表中找到e.index的地址,然后在pinyin.index中查找到en.dat的地址---这时找到所有发音为en的汉字,指针偏移量6,把e.index的地址加上6,获得一个数字80,此时找到第一个汉字嗯,他不是要找的字,但没有关系,可以把后面的一串汉字都取出来.选择"恩",也可以记录下来,这需要前端的一些设计,不展开.

输入法浅析

把字库的存储和查找设计好以后,即可以设计输入法了.虽然现在弄一个输入法出来或许不会有很大价值,比如win10的微软输入法,某狗输入法都很是很成熟的产品,但在思考过程中会对数据的组织,数据类型的设计有不错的学习效果.笔者将在另一专栏对输入法的前端做一些分析

小结

从字库设计上,对数据的访问的几个要素:指针,数据单元(简单数据)的访问,数据所占总空间的实现

预告

输入法前端,词语输入,记忆输入

adv:内容细致,条理清晰,循序渐进,敬请关注笔者数据类型设计专栏,有更多精彩内容呈现.

相关推荐
学困昇1 小时前
C++11中的包装器
开发语言·数据结构·c++·c++11
weixin_4577600010 小时前
Python 数据结构
数据结构·windows·python
明洞日记11 小时前
【数据结构手册002】动态数组vector - 连续内存的艺术与科学
开发语言·数据结构·c++
fashion 道格11 小时前
数据结构实战:深入理解队列的链式结构与实现
c语言·数据结构
xxxxxxllllllshi12 小时前
【LeetCode Hot100----14-贪心算法(01-05),包含多种方法,详细思路与代码,让你一篇文章看懂所有!】
java·数据结构·算法·leetcode·贪心算法
铁手飞鹰12 小时前
二叉树(C语言,手撕)
c语言·数据结构·算法·二叉树·深度优先·广度优先
[J] 一坚14 小时前
深入浅出理解冒泡、插入排序和归并、快速排序递归调用过程
c语言·数据结构·算法·排序算法
司铭鸿14 小时前
祖先关系的数学重构:从家谱到算法的思维跃迁
开发语言·数据结构·人工智能·算法·重构·c#·哈希算法
yk0820..15 小时前
测试用例的八大核心要素
数据结构