ACSII,GB2312,GBK,GB18030,Unicode,UTF8,UTF16,UTF32,BOM区别与转换——字符编码最全总结

来源:互联网 发布:ios远程mac 编辑:程序博客网 时间:2024/06/02 19:20

本人总结的最全字符编码概述,分享学习,涉及ACSII,GB2312,GBK,GB18030,Unicode,UTF8,UTF16,UTF32,BOM。

在做总结时被问到字符编码的知识,由于这块知识没有系统的学习过,所以答的不好,后来本人对字符编码进行了相关的学习,并将学习总结分享给大家,总结中留下了一些问题给大家,希望能带着问题学习,并去自行解决这些问题。由于水平有限,有些知识点比较繁杂,用的不多的我就没有详述,但我提供了相关资料的链接,感兴趣的童鞋们可以进一步的学习。欢迎大家找我讨论和拍砖,有错误的地方我会尽快修改和学习。

你不得不知道的编码知识和发展史

众所周知,计算机最早应用于美国,而美国人使用的语言为英语,英语的灵活在于26个字母描述世界万物。在计算机领域,八个二进制位为一字节,一字节可以组合出256种不同的状态。美国人把这256种状态中,编号从0到31的这32种状态分别规定了特殊的用途,一旦终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上0x10, 终端就换行,遇上0x07, 终端就向人们嘟嘟叫。美国人把这32种(0x20以下的)叫作“控制码”。他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了,于是ASCII码(AmericanStandard Code for Information Interchange,美国信息互换标准代码)诞生了。有同学就问了,不是256种状态么,肿么采用128个?继续听我跟你唠。

         后来,世界各地都开始使用计算机了,但很多国家的语言不是英文,譬如法语字母上和我们的拼音一样有注音的,那肿么办?这时ASCII码没用的那128位闪亮登场(最高位为1的),一些欧洲国家决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。其中0--127表示的符号和ASCII码一样的,不一样的只是128--255的这一段,所以这种就是“ASCII扩展字符集”。

当计算机来到我泱泱大国,遇见我上下五千年历史传承下来的汉字便水土不服了。常用汉字就有6千多个(PS:汉字到底多少个?从甲骨文时期的4k+汉字到清朝《康熙字典》47025个,再到现在的《中华字海》收字85568),256个状态完全不够,这难不倒我们智慧勤劳的中国人民,我们不客气地把那些127号之后的奇异符号们直接干掉,规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出7445种状态其中汉字6763个,非汉字图形字符682个)。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是传说中的“全角”字符,而原来在127号以下的那些就叫“半角”字符了。这样,汉字的基本需求就解决了,“GB2313”就这么诞生了!

(GB2312详细编码:http://baike.baidu.com/view/443268.htm?fromtitle=GB2312&fromid=483170&type=syn

GB2312编码表:http://tools.jb51.net/table/gb2312)

但中国的汉字实在太多了,“鑫森淼焱垚”也就算了,碰上“鱻猋骉犇羴”这些货GB2312就口吐白沫给跪了。为了解决这些问题,干脆不再要求低字节一定是127号之后的内码(GB2312是这样的),只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面字节跟的是什么。结果扩展之后的编码方案被称为 GBK 标准,GBK 采用双字节表示,总体编码范围为8140-FEFE之间,首字节在81-FE之间,尾字节在40-FE之间,剔除XX7F一条线。那么问题来了,为什么剔除7F这条线?GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。GBK编码区分三部分,那么问题来了,都分了哪三部分呢,这三部分都包含什么呢?小提示:汉字区,图形符号区,自定义区

GBK内码查询:http://www.mytju.com/classCode/tools/encode_gb2312.asp

再后来我们55个少数民族同胞们也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了“GB18030”。GB18030编码是一二四字节变长编码的哦,那么问题来了,一二四字节的编码范围都是什么?分别表示哪些字符呢?就这样,我们中华民族的文化就可以在计算机时代传承了。后来,这一系列汉字编码的标准得到了广大程序猿兄弟们的接受认可,于是通称他们“DBCS”(Double ByteCharecter Set 双字节字符集)。在DBCS系列标准里,最大的特点就是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。

因为当时各个国家都像中国这样搞出一套自己的编码标准,结果相互之间谁也不懂谁的编码,谁也不支持别人的编码,连仅相隔150海里作为中国领土不可分割的一部分的台湾,和我们使用着同一种语言,也采用了不同的DBCS 编码方案——Big5。因此麻烦来了,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。就在这时,有两个人站出来说话了,一个叫“统一码联盟”,一个叫ISO,他们决定废除旧制,颁布新规,一统天下。于是,他们废了所有的地区性编码方案,重新搞了一个包括了地球上所有文化、所有字母和符号的编码!统一码联盟的搞出来的就是“Unicode”ISO的就是“UniversalMultiple-Octet Coded Character Set”,简称UCS。后来,双方意识到不需要2套通用的字符集,所以双方开始进行整合,到Unicode2.0时,Unicode的编码和UCS的编码都基本一致。所以,我们不关心Unicode和UCS的爱恨情仇,把他俩看成一个就OK了,感兴趣同学可以进一步的了解学习。

Unicode用数字0x000000 -0x10FFFF来映射世界上的所有字符,Unicode使用了17个平面(plane),即前两位0x00 -0x10表示的17个平面,每个平面65536个码位(0x0000 – 0xFFFF),这样最多可以容纳1114112个字符,或者说有1114112个码位。平面0 被称作BMP(BasicMultilingual Plane),其他16个为辅助平面。Unicode同样也不完美,这里就有两个的问题,一个是,如何才能区别Unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费,文本文件的大小会因此大出二三倍,这是难以接受的。

你不得不知的UTF

Unicode在很长一段时间内无法推广,直到互联网的出现,为解决Unicode如何在网络上传输的问题,于是面向传输的众多UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种Unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。请注意,UTF-8Unicode到底什么关系呢?UTF-8是Unicode的实现方式之一。

=====UTF-8=====

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号。

UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的Unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode码。

下表总结了编码规则,字母x表示可用编码的位

(十六进制)        |        (二进制)
—————————————————————–

0000 0000 - 0000 007F | 0xxxxxxx
0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx10xxxxxx
0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx10xxxxxx 10xxxxxx

跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

举例:已知"严"的Unicode是U+4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(00000800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5

=====UTF-16=====

如果Unicode编码U < 0x10000,即U属于平面0(即BMP),UTF-16编码就是U对应的16位无符号整数。(以下用WORD描述16位无符号整数)

如果Unicode编码U≥0x10000,即U不属于平面0,我们先计算U'=U - 0x10000,然后将U'写成如下格式的20位的二进制:yyyyyyyy yyxx xxxx xxxx,则U的UTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx

举例:Unicode编码U+0x20C30,减去0x10000后,得到0x10C30,写成二进制是:00010000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30。

按照上述规则,Unicode编码0x10000-0x10FFFFUTF-16编码有两个16位无符号整数组成,第一个的WORD6位是110110,第二个WORD的高6位是110111。可见,第一个WORD的取值范围(二进制)是11011000 000000001101101111111111,即0xD800-0xDBFF。第二个的取值范围(二进制)是11011100 000000001101111111111111,即0xDC00-0xDFFF

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

D800DB7F

High Surrogates

高位替代

DB80DBFF

High Private Use Surrogates

高位专用替代

DC00DFFF

Low Surrogates

低位替代

由两个WORD组成的UTF-16编码中,高位替代就是指平面1到平面14中,第一个WORD的编码范围。而高位专用替代是指平面15和平面16UTF-16编码的第一个WORD编码范围。低位替代就是所有平面的第二个WORD的编码范围。

=====UTF-32=====

UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。例如Unicode为U+0x020C30,则其UTF-32表示为0x00 02 0C 30

=====LE & BE(Little endian & Big endian)=====

两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。

字节序有两种,分别是“大端”(BigEndian, BE)和“小端”(Little Endian, LE)。第一个字节在前,就是BE,第二个字节在前就是LE。根据字节序的不同,UTF-16可被实现为UTF-16LE或UTF-16BE,UTF-32可被实现为UTF-32LE或UTF-32BE。

Unicode编码

UTF-16LE

UTF-16BE

UTF32-LE

UTF32-BE

U+0x006C49

49 6C

6C 49

49 6C 00 00

00 00 6C 49

U+0x020C30

43 D8 30 DC

D8 43 DC 30

30 0C 02 00

00 02 0C 30

=====BOM=====

那么问题来了,计算机怎么知道某一个文件到底采用哪一种方式编码?Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,即BOM(Byte Order Mark)来区分字节序,在传输字节流前,先传输被叫做BOM的字符“零宽无中断空格”。用FEFF表示。这正好是两个字节,而且FF比FE大1。

下表是各种UTF编码的BOM:

UTF编码

BOM

UTF-8 BOM

UTF-8  BOM

EF BB BF

UTF-16LE

FF FE

UTF-16BE

FE FF

UTF-32LE

FF FE 00 00

UTF-32BE

00 00 FE FF

=====小实验=====

新建一个文本文件,文件里写一个“高德”的“德”字,依次采用ANSI,Unicode,Unicode big endian 和 UTF-8编码方式保存。

然后,用文本编辑软件UltraEdit中的"十六进制功能",观察该文件的内部编码方式。

1)ANSI:文件的编码就是两个字节"B5C2",这正是"德"的GB2312编码,这也暗示GB2312是采用大头方式存储的。

2)Unicode:编码是四个字节"FFFE B7 5F",其中"FF FE"表明是小头方式存储,真正的编码是5FB7。

3)Unicode bigendian:编码是四个字节"FE FF 5F B7",其中"FE FF"表明是大头方式存储。

4)UTF-8:编码是六个字节"EFBB BF E5 BE B7",前三个字节"EF BB BF"表示这是UTF-8编码,后三个"E5 BE B7"就是"德"的具体编码,它的存储顺序与编码顺序是一致的。

你不得不学习的字符编码的转化:

1. 在网上找到的一些转化方法

C++11与Unicode及使用标准库进行UTF-8、UTF-16、UCS2、UCS4/UTF-32编码转换:

http://www.cppblog.com/Error/archive/2014/09/25/208413.html

utf-8,utf-16和utf-32之间的互相转化:

http://blog.csdn.net/xiayefanxing/article/details/7661970

http://blog.csdn.net/jhqin/article/details/5687505

GB2312和UTF-8之间的相互转化:

http://blog.csdn.net/lilien1010/article/details/7824799

以上都是本人解读材料之后总结了的知识点,由于水平有限,肯定有很多不足甚至错误的地方,我是希望能够起到一个抛砖引玉的作用,希望大家在看的过程中思考和查找资料挑出我的错误或不足,并希望各位发现问题时及时向我拍砖,我会第一时间修改并学习。

另外这部分学习的内容还是蛮多的,另附总结时网上搜集的相关材料链接,方便感兴趣的进行查阅:

“ASCII、Unicode、GBK和UTF-8字符编码的区别联系”http://dengo.org/archives/901

“字符编码笔记”http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

“GB2312、GBK、GB18030”http://www.zhihu.com/question/19677619

“关于Unicode字符集”http://blog.sina.com.cn/s/blog_4b4409c30100vw9t.html

“谈谈Unicode编码”http://www.fmddlmyy.cn/text6.html

“unicode和ucs的区别”http://blog.csdn.net/ultrani/article/details/8432767

“GB2312”         http://baike.baidu.com/view/443268.htm


“GB2312”         http://baike.baidu.com/view/443268.htm 

                           https://en.wikipedia.org/wiki/GB_2312

“GBK”                http://baike.baidu.com/view/931619.htm

“Unicode”         http://baike.baidu.com/view/40801.htm

                            https://en.wikipedia.org/wiki/Unicode

“Short overviewof ISO-IEC 10646 and Unicode”

http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html

0 0