欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

各种编码格式(GB2312,GBK,GB18030,unicode,utf-8)之间的关系

程序员文章站 2022-07-14 19:02:06
...

汉字常用编码格式

为了在屏幕上显示字符。需要下面几个步骤:

  1. 制作所有字符对应的字模。比如大写字母A长什么样。这个模样就是最终显示在屏幕上图形,即我们看到的字符A
  2. 为对所有的字符进行编码。比如大写字母A的编码为0x41.
  3. 由于字符的数量远大于一个字节,所以当字符编码进行存储、传输时,需要给它们指定格式。如怎样确定某个字符的编码是一字节还是多字节。如0xC0 0xEE代表一个字符的编码(0xC0EE)还是两个字符的编码(0xC0、0xEE)。

第1个步骤的工作也就是我们常见的制作字体,比如宋体楷体等。它只关心字符的长相。而第二、三个步骤有些是一起做的。如GB2312GB18030的编码方式。有些是分开的,如unicode编码指做了第而步骤,而将第三步骤交给了utf-8utf-16utf-32等。

计算机在屏幕上显示字符的流程是,加载存储好的文本,通过编码方式解析出字符编码,然后通过字符编码找到对应的字符字模,然后显示。由于计算机的发展是一步步迭代的,由于之前的编码方式不能满足后续的工作需求,所以出现了很多中的编码方式。如我们常见的GB2312utf-8等。下图是常用编码方式之间的关系图:各种编码格式(GB2312,GBK,GB18030,unicode,utf-8)之间的关系

GB18030、GBK、GB2312汉字编码格式

GB2312编码

GB2312编码是第一个汉字编码国家标准,由中国国家标准总局1980年发布,1981年5月1日开始使用。GB2312编码共收录汉字6763个,其中一级汉字3755个,二级汉字3008个。同时,GB2312编码收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。。它使用2个字节编码,编码范围为A1A1~FEFE。其中首字节叫区,尾字节叫位。共A1~FE94个区,每个区包含A1~FE94个位。共94*94=8836个码位。

  1. 1~9区收录除汉字外的682个字符。
  2. 10-15区为空白区,没有使用。
  3. 16-55区收录3755个一级汉字,按拼音排序。
  4. 56-87区收录3008个二级汉字,按部首/笔画排序。
  5. 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号平面;
各种编码格式(GB2312,GBK,GB18030,unicode,utf-8)之间的关系
具体的汉字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. 它是一个变长字节(1~4个字节)的编码方式。
  2. 对于一个字节的编码格式:字节bit[7:0]的bit[7]位必须为0,bit[6:0]这7个位用来编码,即二进制为0b0xxxxxxx
  3. 对于n(n>1)字节(2,3,4个字节)的编码格式:
    1. 用首字节的bit[7:8-n]位置1,bit[8-n-1]置位0表示字节位数。如首字节为0b110xxxxx表示二字节的编码格式。同理0b1110xxxx为三字节格式。剩下的
    2. 除首字节剩下的字节的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编码的步骤如下:

  1. 首先找到unicode字符编码所在上表中的编号范围。如字符A的unicode编码为0x41,位于utf-8 1字节编码格式(0~127)的编号范围。
  2. 将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
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。各种编码格式(GB2312,GBK,GB18030,unicode,utf-8)之间的关系

相关标签: 其他