各种编码格式(GB2312,GBK,GB18030,unicode,utf-8)之间的关系
汉字常用编码格式
为了在屏幕上显示字符。需要下面几个步骤:
- 制作所有字符对应的字模。比如大写字母
A
长什么样。这个模样就是最终显示在屏幕上图形,即我们看到的字符A
。 - 为对所有的字符进行编码。比如大写字母
A
的编码为0x41. - 由于字符的数量远大于一个字节,所以当字符编码进行存储、传输时,需要给它们指定格式。如怎样确定某个字符的编码是一字节还是多字节。如
0xC0 0xEE
代表一个字符的编码(0xC0EE)还是两个字符的编码(0xC0、0xEE)。
第1个步骤的工作也就是我们常见的制作字体,比如宋体
、楷体
等。它只关心字符的长相。而第二、三个步骤有些是一起做的。如GB2312
、GB18030
的编码方式。有些是分开的,如unicode编码指做了第而步骤,而将第三步骤交给了utf-8
、utf-16
、utf-32
等。
计算机在屏幕上显示字符的流程是,加载存储好的文本,通过编码方式解析出字符编码,然后通过字符编码找到对应的字符字模,然后显示。由于计算机的发展是一步步迭代的,由于之前的编码方式不能满足后续的工作需求,所以出现了很多中的编码方式。如我们常见的GB2312
、utf-8
等。下图是常用编码方式之间的关系图:
GB18030、GBK、GB2312汉字编码格式
GB2312编码
GB2312编码是第一个汉字编码国家标准,由中国国家标准总局1980年发布,1981年5月1日开始使用。GB2312编码共收录汉字6763个,其中一级汉字3755个,二级汉字3008个。同时,GB2312编码收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。。它使用2个字节编码,编码范围为A1A1~FEFE
。其中首字节叫区,尾字节叫位。共A1~FE
94个区,每个区包含A1~FE
94个位。共94*94=8836个码位。
- 1~9区收录除汉字外的682个字符。
- 10-15区为空白区,没有使用。
- 16-55区收录3755个一级汉字,按拼音排序。
- 56-87区收录3008个二级汉字,按部首/笔画排序。
- 88-94区为空白区,没有使用。
GBK编码
GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。GBK中的K
为汉语拼音扩
的声母,即扩展的含义。英文全称Chinese Internal Code Specification。GBK编码标准兼容GB2312。GB2312基本满足了汉字的计算机处理需要,但对于人名、古汉语等方面出现的罕用字,GB2312不能处理,这导致了后来GBK及GB18030汉字字符集的出现。GBK采用双字节表示,总体编码范围为8140-FEFE
,首字节在81-FE
之间,尾字节在40-FE
之间,剔除xx7F
一条线。总计23940个码位,共收入21886个汉字和图形符号.
GB18030编码
在2000年,GB18030取代GBK1.0,正式国家标准。它的主要特点是在GBK基础上增加了CJK统一汉字扩充A的汉字(GB18030-2000)。后又在此基础上增加了CJK统一汉字扩充B的汉字(GB18030-2005)。
GB18030编码格式有单字节、双字节、四字节三种方案。其中单、双字节的编码和GBK完全兼容。4字节的编码内容为CJK扩展A的6582个汉字。
unicode编码及格式
unicode官网:https://www.unicode.org
上面提到的GBxxx都是我们国家定义的标准,当然只针对我们公家的汉字以及ASCII等。而unicode的出现是为了解决全球的字符编码。以满足跨语言、跨平台进行文本转换、处理的要求。这样我们就可以在一篇文章中用n国语言来写我爱你了。
unicode与UCS-2、UCS-4
早期 Unicode 在编制通用字符集之时,ISO 组织也在做同样的事情,ISO 开展了 ISO/IEC 10646 项目,名字叫“ Universal Multiple-Octet Coded Character Set”,中文译为“通用多八位编码字符集”,英文简称UCS。后来双方整合,到 Unicode 2.0 时,Unicode 编码和 UCS 编码都基本一致。
UCS-2 采用 16 位存储空间,两个字节编码每个字符,而 UCS-4 采用 4 个字节(实际上只用了 31 位,最高位必须为 0)编码。
UCS-4 根据最高位为 0 的最高字节分成27=128个组(group)。每个组再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行(rows),每行包含256个单元(cells)。当然同一行的单元只是最后一个字节不同,其余都相同。
0组的0号平面被称作Basic Multilingual Plane,即基本多文种平面,简写BMP。可知BMP区域内的字符只使用了两个字节,码位从 U+0000 至 U+FFFF。它实际上就是 UCS-2 的全部编码范围,后来因为码位不够用才扩展为 UCS-4。
17个平面中目前只用到0号、1号、2号和14号平面,其中汉字在0号平面和2号平面,其它文字在0号、1号和14号平面;
具体的汉字Unicode编码范围见此链接:https://www.qqxiuzi.cn/zh/hanzi-unicode-bianma.php.
utf-8、utf-16、utf-32编码格式
要说unicode与utf-8、utf-16、utf-32之间的关系,我们须明确编码格式
=编码
+存储格式
。其中编码
对应文章开头提到的第二个步骤,存储格式
对应第三个步骤。unicode只是编码,不涉及存储格式。而utf-x
是基于unicode编码的编码格式。清楚这点,我们也就清楚了utf-8、utf-16、utf-32三者的关系。它们的都使用unicode编码,那肯定存储格式存在差异,因此起了三个名呗。说到这儿,会有一个疑问?那GB2312、GBK等的编码
和存储格式
分别是什么了?其实,GB2312、GBK等是将编码
和存储格式
整合到了一起。因为GB2312的编码是从A1A1~FEFE
,它和ASCII(0x00~0x7F)在字节流上本身就不冲突。当文本中的字节小于0x80
,他一定为ASCII码。而当文本中的字节大于0x80
时,此字节加下一个字节共同表示一个汉字。而unicode编码就不同了,它的字符编码中的每个字节都可以小于0x80
,那么它的这个字节代表ASCII呢还是和其他字节组合表示一个别的字符(如汉字)?没法确定,所以必须要有存储格式
。下面将谈论它们的编码格式。
utf-8编码格式
utf-8官网:http://www.utf-8.com/
utf-8(Unicode Transformation Format 8-bit)是一种可变宽度的编码格式。用1~4个字节表示一个Unicode字符编码。它可以表示unicode字符集中的所有字符。
utf-8编码格式:
- 它是一个变长字节(1~4个字节)的编码方式。
- 对于一个字节的编码格式:字节bit[7:0]的bit[7]位必须为0,bit[6:0]这7个位用来编码,即二进制为
0b0xxxxxxx
。 - 对于n(n>1)字节(2,3,4个字节)的编码格式:
- 用首字节的
bit[7:8-n]位置1,bit[8-n-1]置位0
表示字节位数。如首字节为0b110xxxxx
表示二字节的编码格式。同理0b1110xxxx
为三字节格式。剩下的 - 除首字节剩下的字节的
bit[7:6]=0b10
.其余位用于编码。
如编码0b1110xxxx,0b10xxxxxx,0b10xxxxxx
表示一个三字节的编码格式,其中x代表的是可编码的位置。可以计算出三字节编码格式共有2^24个编码位置。
如表是1~4字节的具体编码格式表:
- 用首字节的
utf-8 n字节编码格式 | 编号范围 |
---|---|
0b0xxxxxxx | 0x00~0x7F |
0b110xxxxx 0b10xxxxxx | 0x80~0x7FF |
0b1110xxxx 0b10xxxxxx 0b10xxxxxx | 0x800~0xFFFF |
0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx | 0x10000~0x10FFFF |
上面是存储格式,那如何将unicode字符编码填充到utf-8的格式里了?unicode
编码转化为utf-8
编码的步骤如下:
- 首先找到
unicode字
符编码所在上表中的编号范围。如字符A
的unicode编码为0x41
,位于utf-8 1字节编码格式(0~127)的编号范围。 - 将unicode字符编码的二进制位从右到左填入上表中的
x
位(注意要去掉高位的0)。如字符A
(0x41=0b01010001)去掉高位0则为0b1010001。将其填入0b0xxxxxxx后则为0b01010001
.也就是utf-8字符A
的编码值。
再举个例子:汉字郭
的unicode字符编码为0x90ED
(0b10010000,11101101),由0x800 < 0x90ED < 0xFFFF
得到它的编码格式为utf-8的3字节编码格式。然后将其二进制0b10010000,11101101
填入0b1110xxxx 0b10xxxxxx 0b10xxxxxx
后为0b11101001,10000011,10101101=0xE983AD
.也就是utf-8字符郭
的编码值。
有了utf-8与unicode之间的关系,就很容易写出转换程序。下面是c语言实现的utf-8编码转unicode编码
/*
* utf-8字符编码转化为unicode编码。(2字节模式)
* pIn:要转换的字符串首地址
* charsize:接收返回的utf8字符的字节数(1~3)
* pOut:获取到的UCS2编码,pOut[0]为首字节。
*/
int Utf8ToUCS2(const char *pIn,char *charsize,char *pOut)
{
uint8_t firstValue = *pIn;
char *pUCS2 = pOut;
if(firstValue < 0x80){//0~127 ASCII 1byte
pUCS2[0] = 0;
pUCS2[1] = *pIn;
*charsize = 1;
}
else if( (firstValue & 0xE0) == 0xC0){//128~2047 2byte
if( (pIn[1] & 0xC0) != 0x80){
return -1;
}
pUCS2[0] = (pIn[0] & 0x1F) >> 2;
pUCS2[1] = (pIn[0] << 6) + (pIn[1] & 0x3F);
*charsize = 2;
}
else if( (firstValue & 0xF0) == 0xE0){//2048~65536 3byte
if( (pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80){
return -1;
}
pUCS2[0] = (pIn[0] << 4) + ((pIn[1] & 0x3F) >> 2);
pUCS2[1] = (pIn[1] << 6) + (pIn[2] & 0x3F);
*charsize = 3;
}
else {//>3byte不处理,汉字用不到4字节的编码
*charsize = 0;
return -1;
}
return 0;
}
汉字编码转换工具及编码表
1.千千秀字:https://www.qqxiuzi.cn/daohang.htm 这个网站上有编码之间的转换、编码值查询、汉字码值表等编码相关的工具。
关于技术交流
此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。