云社区 博客 博客详情

常用字符集介绍和编码转换原理

瘸子那条好腿 发表于 2020-06-12 16:39:12 2020-06-12
1
1

【摘要】 常用字符集介绍和编码转换原理

1.  GB2312编码介绍

1.1  基本信息

《信息交换用汉字编码字符集》是由我国国家标准总局1980年发布,198151日开始实施的一套国家 汉字编码字符集标准,标准号是GB 2312—1980。它是计算机可以识别的编码,适用于汉字处理、汉字通信等系统之间的信息交换。基本集共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位。每个区位上只有一个字符,因此可用所在的区和位来对汉字进行编码,称为区位码

这个码是唯一的,不会有重码字。把换算成十六进制的区位码加上2020H,就得到国标码。国标码加上8080H,就得到常用的计算机机内码

1995年又颁布了《汉字编码扩展规范》(GBK)。GBKGB 2312—1980国家标准所对应的内码标准兼容,同时在字汇一级支持ISO/IEC10646—1GB 13000—1的全部中、日、韩(CJK)汉字,共计20902字。

信息交换用汉字编码字符集和汉字输入编码之间的关系是,根据不同的汉字输入方法,通过必要的设备向计算机输入汉字的编码,计算机接收之后,先转换成信息交换用汉字编码字符,这时计算机就可以识别并进行处理;汉字输出是先把机内码转成汉字编码,再发送到输出设备


1.2  GB标准

GB2312GB2312-80是一个简体中文字符集的中国国家标准,全称为《信息交换用汉字编码字符集·基本集》,又称为GB0,由中国国家标准总局发布,198151日实施。GB2312编码通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB2312

GB2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时,GB2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682全角字符

GB2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。

对于人名、古汉语等方面出现的罕用字,GB2312不能处理,这导致了后来GBKGB18030汉字字符集的出现。

1.3  分区表示

GB 2312中对所收汉字进行了分区处理,每区含有94个汉字/符号。这种表示方式也称为区位码

01-09区为特殊符号。

16-55区为一级汉字,按拼音排序。

56-87区为二级汉字,按部首/笔画排序。

10-15区及88-94区则未有编码

举例来说,字是GB2312之中的第一个汉字,它的区位码就是1601

1.4  字节结构

在使用GB2312的程序中,通常采用EUC储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指EUC-CN表示法。

每个汉字及符号以两个字节来表示。第一个字节称为高位字节(也称区字节),第二个字节称为低位字节(也称位字节)。

高位字节使用了0xA1-0xF7(01-87区的区号加上0xA0)低位字节使用了0xA1-0xFE(01-94加上 0xA0) 由于一级汉字从16区起始,汉字区的高位字节的范围是0xB0-0xF7低位字节的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE

例如字在大多数程序中,会以两个字节0xB0(第一个字节) 0xA1(第二个字节)储存。区位码=字节+位字节(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。

2.  通用字符集UCS

2.1  定义

通用字符集(Universal Character SetUCS)是ISO制定ISO 10646(或称ISO/IEC 10646)标准所定义的字符编码方式,采用4字节编码。又称Universal Multiple-Octet Coded Character Set,大陆译为通用多八位编码字符集,台湾译为广用多八位元编码字元集

2.2  概要

通用字符集是所有包括了其他字符集。它保证了与其他字符集的双向兼容,即,如果你将任何文本字符串翻译到UCS格式,然后再翻译回原编码,你不会丢失任何信息。UCS包含了已知语言的所有字符。除了拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语、乔治亚语,还包括中文、日文、韩文这样的象形文字,UCS还包括大量的图形、印刷、数学、科学符号。

ISO 10646定义了一个31位的字符集。ISO 10646-1标准第一次发表于1993年,现在的公开版本是ISO/IEC 10646-1:2000ISO 10646-22001年发表。UCS不仅给每个字符分配一个代码,而且赋予了一个正式的名字。表示一个UCSUnicode值的十六进制数通常在前面加上“U+”,例如“U+0041”代表字符“A”

2.3  实现级别

并不是所有的系统都需要支持像组合字符这样的的先进机制。因此ISO 10646指定了如下三种实现级别:

级别1:不支持组合字符和谚文字母字符。

级别2:类似于级别1,但在某些文字中,允许一列固定的组合字符,因为如果没有最起码的几个组合字符,UCS就不能完整地表达这些语言。

级别3:支持所有的通用字符集字符,如,可以在任意一个字符上加上一个箭头或一个鼻音化符号.

2.4  UNICODE兼容关系

通用字符集是与UNICODE同类的组织,UCS-2UNICODE兼容 位数:它有UCS-2UCS-4两种格式,分别是2字节和4字节。 范围:UCS-4只是在UCS-2前面加了0×0000

3.  unicode编码介绍

Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。随着计算机工作能力的增强,Unicode也在面世以来的十多年里得到普及。

 

备注:

统一码联盟(英语:The Unicode Consortium)是一个致力于开发,维护,发展全球通用软件标准和数据格式,特别是维护Unicode编码标准的非牟利机构。

3.1  基本简介

Unicode 是基于通用字符集Universal Character Set)的标准来发展,并且同时也以书本的形式(The Unicode Standard,目前第五版由Addison-Wesley Professional出版,ISBN-10: 0321480910)对外发表。

20067的最新版本的 Unicode 5.0版本。 2005331推出的Unicode 4.1.0 。另外,5.0 Beta20051212日推出,5.2版本(unicode standard)于2009101日正式推出,以供各会员评价。

目前Unicode标准,6.1版已发布(2012131日)。在unicode联盟网站上可以查看完整的6.1的核心规范。

Unicode定义了大到足以代表人类所有可读字符的字符集。

Java语言就用到了Unicode编码,从而实现了该语言的国际通用性。

3.2  编码实现

大概来说,Unicode 编码系统可分为编码方式和实现方式两个层次。

3.2.1  编码方式

Unicode是国际组织统一码联盟制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位(码位就是可以分配给字符的数字)。UTF-8UTF-16UTF-32都是将数字转换到程序数据的编码方案。

 

拓展了解:

通用字符集(Universal Character SetUCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-44个字节编码。

历史上存在两个独立的尝试创立单一字符集的组织,即国际标准化组织ISO)和多语言软件制造商组成的统一码联盟。前者开发的 ISO/IEC 10646 项目,后者开发的统一码项目。因此最初制定了不同的标准。

1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFFUCS-4编码赋值,以使得两者保持一致。两个项目仍都存在,并独立地公布各自的标准。但统一码联盟和ISO/IEC JTC1/SC2都同意保持两者标准的码表兼容,并紧密地共同调整任何未来的扩展。在发布的时候,Unicode一般都会采用有关字码最常见的字型,但ISO 10646一般都尽可能采用Century字型。

UCS-4根据最高位为0的最高字节分成2^7=128group。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256 row),每行有256个码位(cell)。group 0的平面0被称作BMPBasic Multilingual Plane)。将UCS-4BMP去掉前面的两个零字节就得到了UCS-2。每个平面有2^16=65536个码位。Unicode计划使用了17个平面,一共有17*65536=1114112个码位。在Unicode 5.0.0版本中,已定义的码位只有238605个,分布在平面0、平面1、平面2、平面14、平面15、平面16。其中平面15和平面16上只是定义了两个各占65534个码位的专用区(Private Use Area),分别是0xF0000-0xFFFFD0x100000-0x10FFFD。所谓专用区,就是保留给大家放自定义字符的区域,可以简写为PUA

平面0也有一个专用区:0xE000-0xF8FF,有6400个码位。平面00xD800-0xDFFF,共2048个码位,是一个被称作代理区(Surrogate)的特殊区域。代理区的目的用两个UTF-16字符表示BMP以外的字符。在介绍UTF-16编码时会介绍。

如前所述在Unicode 5.0.0版本中,238605-65534*2-6400-2048=99089。余下的99089个已定义码位分布在平面0、平面1、平面2和平面14上,它们对应着Unicode目前定义的99089个字符,其中包括71226个汉字。平面0、平面1、平面2和平面14上分别定义了52080341943253337个字符。平面243253个字符都是汉字。平面0上定义了27973个汉字。

3.2.2  实现方式

Unicode中:汉字对应的数字是23383。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8UTF-16UTF-32UTF“UCS Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。例如,汉字对应的数字是0x6c490x5b57,而编码的程序数据是:

BYTE data_utf8[] = {0xE6, 0xB1, 0x89, 0xE5, 0xAD, 0x97}; // UTF-8编码

WORD data_utf16[] = {0x6c49, 0x5b57}; // UTF-16编码

DWORD data_utf32[] = {0x6c49, 0x5b57}; // UTF-32编码

这里用BYTEWORDDWORD分别表示无符号8位整数,无符号16位整数和无符号32位整数。UTF-8UTF-16UTF-32分别以BYTEWORDDWORD作为编码单位。汉字UTF-8编码需要6个字节。汉字UTF-16编码需要两个WORD,大小是4个字节。汉字UTF-32编码需要两个DWORD,大小是8个字节。根据字节序的不同,UTF-16可以被实现为UTF-16LEUTF-16BEUTF-32可以被实现为UTF-32LEUTF-32BE。下面介绍UTF-8UTF-16UTF-32、字节序和BOM

4.  UTF介绍

4.1  基本介绍

事实证明,对可以用ASCII表示的字符使用UNICODE并不高效,因为UNICODEASCII占用大一倍的空间,而对ASCII来说高字节0对他毫无用处。为了解决这个问题,就出现了一些中间格式的字符集,他们被称为通用转换格式,即UTFUniversal Transformation Format)。常见的UTF格式有:UTF-7, UTF-7.5, UTF-8,UTF-16, 以及 UTF-32

对于英语国家来说,UNICODE内码非常浪费空间,对于UCS-2 浪费50%的存储空间,对于 UCS-4 则浪费了70%的存储空间. 而且还有一个更大的问题, UNICODE的内码中含有很多 '\0', 原有的C标准库函数没办法处理这些字符串.于是有人发明了一种针对UNICODE的变换规则,UNICODE字符串中的0去除. 注意这个变换规则不是通过查表实现的,而只要用一些位移操作就可以实现. 这就是UTF8. UTF8 只是 UNICODE内码在存储/传输时的状态. 而从GB2312编码转换到UNICODE编码需要查表. UTF8 UNICODE 的关系 GB2312 UNICODE的关系有本质的不同. UTF8 UNICODE 是一个人的两个面孔, GB2312 UNICODE 是两个人. 所以,要实现UTF8编码到GB2312编码的转换必须先把 UTF8编码还原为UNICODE编码,再通过查表的方式,UNICODE编码转化为GB2312编码.

4.2  编码原理

4.2.1  UTF-8

UTF-8以字节为单位对Unicode进行编码。从UnicodeUTF-8的编码方式为:Unicode二进制从低位往高位取出二进制数字,每次取6位,如上述的二进制就可以分别取出为如下示例所示的格式,前面按格式填补,不足8位用0填补。

 

Unicode编码(16进制) 

UTF-8 字节流(二进制)

000000 - 00007F 

0xxxxxxx

000080 - 0007FF 

110xxxxx 10xxxxxx

000800 - 00FFFF 

1110xxxx 10xxxxxx      10xxxxxx

010000 - 10FFFF 

11110xxx 10xxxxxx      10xxxxxx 10xxxxxx



UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。

1字的Unicode编码是0x6C490x6C490x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89

2Unicode编码0x20C300x010000-0x10FFFF之间,使用用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0

4.2.2  UTF-16

UTF-16编码以16位无符号整数为单位。我们把Unicode编码记作U。编码规则如下:

如果U<0x10000UUTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。

如果U≥0x10000,我们先计算U'=U-0x10000,然后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxxUUTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx

为什么U'可以被写成20个二进制位?Unicode的最大码位是0x10ffff,减去0x10000后,U'的最大值是0xfffff,所以肯定可以用20二进制位表示。例如:Unicode编码0x20C30,减去0x10000后,得到0x10C30,写成二进制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30

按照上述规则,Unicode编码0x10000-0x10FFFFUTF-16编码有两个WORD,第一个WORD的高6位是110110,第二个WORD的高6位是110111。可见,第一个WORD的取值范围(二进制)是11011000 0000000011011011 11111111,即0xD800-0xDBFF。第二个WORD的取值范围(二进制)是11011100 0000000011011111 11111111,即0xDC00-0xDFFF

为了将一个WORDUTF-16编码与两个WORDUTF-16编码区分开来,Unicode编码的设计者将0xD800-0xDFFF保留下来,并称为代理区(Surrogate): 

D800DB7F 

High Surrogates 

高位替代

DB80DBFF 

High Private Use      Surrogates 

高位专用替代

DC00DFFF

Low Surrogates 

低位替代

高位替代就是指这个范围的码位是两个WORDUTF-16编码的第一个WORD。低位替代就是指这个范围的码位是两个WORDUTF-16编码的第二个WORD。那么,高位专用替代是什么意思?我们来解答这个问题,顺便看看怎么由UTF-16编码推导Unicode编码。

如果一个字符的UTF-16编码的第一个WORD0xDB800xDBFF之间,那么它的Unicode编码在什么范围内?我们知道第二个WORD的取值范围是0xDC00-0xDFFF,所以这个字符的UTF-16编码范围应该是0xDB80 0xDC000xDBFF 0xDFFF。我们将这个范围写成二进制:

1101101110000000 11011100 00000000 - 1101101111111111 1101111111111111

按照编码的相反步骤,取出高低WORD的后10位,并拼在一起,得到

1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111

0xe0000-0xfffff,按照编码的相反步骤再加上0x10000,得到0xf0000-0x10ffff。这就是UTF-16编码的第一个WORD0xdb800xdbff之间的Unicode编码范围,即平面15和平面16。因为Unicode标准将平面15和平面16都作为专用区,所以0xDB800xDBFF之间的保留码位被称作高位专用替代。

4.2.3  UTF-32

UTF-32编码以32位无符号整数为单位。UnicodeUTF-32编码就是其对应的32位无符号整数。

字节序

根据字节序的不同,UTF-16可以被实现为UTF-16LEUTF-16BEUTF-32可以被实现为UTF-32LEUTF-32BE。例如:

Unicode编码

UTF-16LE 

UTF-16BE 

UTF32-LE 

UTF32-BE

0x006C49

49 6C

6C 49

49 6C 00 00 

00 00 6C 49

0x020C30 

43 D8 30 DC 

D8 43 DC 30 

30 0C 02 00

00 02 0C 30

那么,怎么判断字节流的字节序呢?Unicode标准建议用BOMByte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符"零宽无中断空格"。这个字符的编码是FEFF,而反过来的FFFEUTF-16)和FFFE0000UTF-32)在Unicode中都是未定义的码位,不应该出现在实际传输中。

 

下表是各种UTF编码的BOM

UTF编码

Byte Order      Mark

UTF-8

EF BB BF

UTF-16LE 

FF FE

UTF-16BE 

FE FF

UTF-32LE 

FF FE 00 00

UTF-32BE 

00 00 FE FF

 

4.  转换原理

由于各种编码之间不存在互相变换的算法,只能通过查表解决转换问题。自编代码进行转换在嵌入式系统中最有实际意义,该方法具有最方便的移植特性和最小的代码量。需要解决的主要技术问题有:

·获取所需的编码转换表

·实现码表的快速搜索算法(UTF-8GB码才需要,其实就是折半查找)

·待转换字符串中的中/西文字符判别

由于折半查找要求码表是事先排序的,正变换和反变换需要各有一张转换表。转换表可以从开源软件中获取也可以自己编段程序生成一份。

由于非unicode编码字串中的西文字母只有一字节/字符,而汉字是2字节/字符,需要在转换时区别对待。判断方法在本文的前面部分有介绍。

GB2312码转unicode时,由于转换表是按区位表排列的,可以直接由汉字的GB码通过计算得到转换表中的行列值,计算公式为:

Row = MSB - 0xA0 - 16

Col = LSB – 0xA0

由于转换表是从汉字区开始的,即第一个汉字是,开始行不是0,而是16,所以要从行值中减去一个偏移量。得到行列值后,可以直接取回表中的unicodeUnicode = CODE_LUT[Row][Col];


登录后可下载附件,请登录或者注册

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:huaweicloud.bbs@huawei.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
评论文章 //点赞 收藏 1
点赞
分享文章到微博
分享文章到朋友圈

上一篇:PostgreSQL 重复 数据清洗 优化

下一篇:PostgreSQL Oracle 兼容性之 - 内核自带的兼容函数

评论 (1)


听风吹雪

1楼2020-06-28 19:26:31

登录后可评论,请 登录注册

评论