基本知识
utf8 本质是 unicode 标准的一种实现方式,utf8 编码和 unicode 字符码是有相互转换的规则的。gbk 码与 unicode 字符码是没有设计有规律的对应关系的,即没有相互转换的规则。所以想要实现 utf8 和 gbk 编码互转需要依靠查表法,即 utf8 转 gbk 编码需要先按规则转换成 unicode 字符码,再通过查表获取该 unicode 字符码对应的 gbk 码,同样的 gbk 转 utf8 编码也需要先查表获取对应的 unicode 字符码,再按照规则转换成 utf8 编码。
获取 gbk 和 unicode 的编码对应表
这里介绍两个编码对应表,分别是 cp936 编码表,和汉字 unicode 编码表,包含的汉字都是基本汉字,一共 20902 个。除去 ascii 字符外,这些编码中包括的汉字都是两个字节的长度。 其中 cp936 编码表是按照递增的 gbk 编码值为索引的,对应值是 unicode 编码,(由于 gbk 的编码并不是连续的,所有索引之间往往有空缺),而汉字 unicode 编码表是以递增的 unicode 汉字字符码为索引的,对应值是 gbk 编码(unicode 编码范围 4e00-9fa5) 。
实现互转
在获取编码表之后,想要实现 gbk 和 unicode 互转的功能就没有什么难度了,最简单的方法就是定义一个编码数组包含 gbk 和 unicode 的映射关系,然后对所需要转换的编码进行查表,获取对应编码再拼接成一个数据串就完成了。
采用 cp936 编码表:
需要定义一个 short 型二维数组存放 gbk 和 unicode 的映射关系(因为 gbk 编码值不是递增的,中间有很多未使用的值),无论是 gbk 转 unicode 还是 unicode 转 gbk 使用时最长需要遍历整个数组。
采用汉字 unicode 编码表:
需要定义一个 short 型一维数组,存放从 4e00-9fa5 直接的汉字字符,unicode 的编码字符减去 4e00 既是一维数组的索引。在 unicode 转 gbk 时可以直接计算出对应 gbk 的索引值,直接获取该编码值即可。在 gbk 转 unicode 的时候最长需要遍历整个数组。
明显可以看出来,使用汉字 unicode 编码表的时间复杂度和空间复杂度是低于使用 cp936 编码表的,不做优化的话,这里肯定选择汉字 unicode 编码表。
优化占用空间
缺点:
上面虽然实现了 gbk 和 unicode 的互相转换,但是在程序里面定一个 20902 长度的 short 数组还是太臃肿了,而且在某些情况嵌入式系统中是不允许定义这么多的。
优化:
这里我们可以把编码表改造成字库文件。在带操作系统的环境下以读取文件的方式来读取字符编码。在嵌入式系统中没有文件系统的环境下,我们可以把字库文件烧入 flash,以读取 flash 的方式来读取字符编码。
制作字库文件:
汉字 unicode 编码表制作字库文件并不复杂,新建一个 .txt 文件(以 ansi 格式打开),将 unicode 4e00-9fa5 对应的 gbk 编码依次复制到文件中保存,修改名字为 gbk.bin 就可以了。(这里 gbk.bin 是没有文件头和校验值的,直接按照索引读取就行,需要注意的事这里不能有换行符)
优化查找编码速度
缺点:
使用 gbk.bin 优化了程序占用的空间,但是 gbk 转 unicode 的时候最长仍需要遍历整个字库文件,而且有一部分常用的字符是在 4e00-9fa5 后段部分的,这就导致了很多情况下查找效率的降低。
优化:
汉字 unicode 编码表是根据 unicode 排序的,cp936 编码表是根据 gbk 排序的,我们可以将 cp936 编码表也制作成字库 cp936.bin,在 gbk 转 unicode 的时候使用字库 cp936.bin,在 unicode 转 gbk 时使用 gbk.bin,这样互转都是按照编码索引转换成字库索引直接读取对应字库,就省去了遍历的过程。
制作字库文件:
cp936 编码表中的一部分 gbk unicode 0x81fd 0x4fa1 0x81fe 0x4fa2 0x8240 0x4fa4 0x8241 0x4fab
如上所示,gbk 的索引是不连续的,81fe 后面接的是 8240,实际上整个 cp936 中类似于此的断层大概有一百多处,每处缺少的数目是不一样的,这里我们可以通过编写程序将其补全索引,对于的 unicode 的值设置成 0000,并写入 gbk.bin 文件(这里对于大的断层我们进过滤,并在后面的索引计算中进行特殊处理,以免补出来的 bin 文件过大)。
优化字串转换速度
上面都是单个编码去计算索引,读取文件获取编码返回,在大量的字符串转换的时候会有很多冗余的操作,我们可以进行流程上的优化,把所有需要获取的字符编码先全部转换成索引,然后在去字库中一次将所有需要的内容都读出来统一返回,这样减少了流程上的调用。
utf8 和 unicode 的互转
gbk 和 unicode 的互转已经实现了,剩下的就是 utf8 和 unicode 的转换,这部分就有固定的转换规则如下:
utf8 编码规则:如果只有一个字节则其最高二进制位为 0,如果是多字节,其第一个字节从最高位开始,连续的二进制位值为 1,1 的个数决定了其编码的字节数,其余各字节均以 10 开头。
// unicode6.1定义范围:0~10 ffff // 20 0000 ~ 3ff ffff 和 400 0000 ~ 7fff ffff 属于 ucs-4,utf8 现在已经弃用了这部分内容 --------------------------------------------------------------------------------- n | unicode (十六进制) | utf - 8 (二进制) --+-----------------------+------------------------------------------------------ 1 | 0000 0000 - 0000 007f | 0xxxxxxx 2 | 0000 0080 - 0000 07ff | 110xxxxx 10xxxxxx 3 | 0000 0800 - 0000 ffff | 1110xxxx 10xxxxxx 10xxxxxx 4 | 0001 0000 - 0010 ffff | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx --------------------------------------------------------------------------------- // 以下部分弃用 5 | 0020 0000 - 03ff ffff | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 6 | 0400 0000 - 7fff ffff | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx ---------------------------------------------------------------------------------
以上就是utf8和gbk编码互转实现解析的详细内容,更多关于utf8 gbk编码互转的资料请关注代码网其它相关文章!
发表评论