作者 | Aledsan
责编 | 王晓曼
出品 | CSDN博客
基础概念
1、字符
字符指类字形单位或符号,包括字母、数字、运算符号、标点符号和其他符号,以及一些功能性符号。一般来说我们称某个字符集里面的字符,叫xx字符,如ASCII字符集里面的ASCII字符,GB2312字符集里面的GB2312字符。
2、字符集
字符集(Character Set、Charset),字面上的理解就是字符的集合,是一个自然语言文字系统支持的所有字符的集合。字符是各种文字和符号的总称,包括文字、数字、字母、音节、标点符号、图形符号等。例如ASCII字符集,定义了128个字符;GB2312字符集定义了7445个字符。而字符集准确地来说,指的是已编号的字符的有序集合(但不一定是连续的,后文有详细介绍)。
常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。
3、码位
在字符编码术语中,码位(code point)或称编码位置、码点,是组成码空间(或代码页)的数值。例如,ASCII码包含128个码位,范围是016进制到7F16进制,扩展ASCII码包含256个码位,范围是016进制到FF16进制,而Unicode包含1,114,112个码位,范围是016进制到10FFFF16进制。Unicode码空间划分为17个Unicode字符平面(基本多文种平面,16个辅助平面),每个平面有65,536(= 216)个码位。因此Unicode码空间总计是17 × 65,536 = 1,114,112. —解释来源于维基百科。
4、字符编码
字符编码(Character Encoding),是把字符集中的字符按一定方式编码为某指定集合中的某一对象的过程(比如将字符编码为由0和1两个数字所组成的位串模式、由0~9十个数字所组成的自然数序列或电脉冲等),亦即在字符集与指定集合两者之间建立一个对应关系(即映射关系)的过程。这是信息处理的一项基础技术。常见的例子包括将拉丁字母表编码成摩斯电码和ASCII码。
PS:这里我们计算机这里字符编码肯定是用二进制来编码的。
看完这四个概念,你应该要明白,它们之间的关系,以ASCII为例,下图解释它们之间关系。
这里细说一下,码位就是这个字符集里面字符的一个表示位置,通俗来说,码位就是一般跟字符集绑在一起,字符编码是把字符集中的字符编码为特定的二进制数,以便在计算机中存储。这个二进制数就叫xx码。
字符集编码分类总结
在说字符集编码之前,先明确一个观点,字符集编码与字符集是两个不同层面的概念:
(1)charset 是character set 的简写,即字符集。
(2)encoding 是 charsetencoding 的简写,即字符集编码,简称编码。
1、ASCII编码
ASCII(美国信息交换标准代码)是基于拉丁字母(就是我们现在的英文字母)的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本延伸美国标准信息交换码则可以部分支持其他西欧语言,并等同于国际标准ISO/IEC 646。
ASCII由电报码发展而来。第一版标准发布于1963年,1967年经历了一次主要修订,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(一些终端提供了扩展,使得这些字符可显示为诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符。用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。
每个ASCII字符占用1个字节(8bits),共有128位字符或符号,使用7位二进制数(剩下的1位二进制为0,即高位为0)来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符。
缺点:ASCII的最大缺点是只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如naïve、café、élite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。而EASCII(即扩展的ASCII码,利用8位的高位设为1进行扩展)虽然解决了部分西欧语言的显示问题,但对更多其他语言依然无能为力。因此现在的操作系统内码(稍后会讲)基本已经抛弃ASCII码而转用Unicode码。
ASCII码表 :http://www.asciitable.com
2、GB2312编码
前面可以看到ASCII码即使进行了扩展也能表示的字符也很少,尤其是当需要计算机显示存储中文的时候,就需要一种对中文进行编码的字符集,GB 2312就是解决中文编码的字符集,由国家标准委员会发布。那个时候当中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存,于是想到把那些ASCII码中127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。这种汉字方案叫做 “GB2312”。GB2312 是对ASCII 的中文扩展。兼容ASCII。
这里的A GB2312码是0xA3C1,0xA3和0xC1都是高于127的,所以判断它是个全角字符,另外我们可以观察到所有的GB2312编码表中的GB2312码每个字节都是大于0xA0的,这个就是为了保证能区分是否为ASCII码,小于127的字节就按照ASCII码标准,碰到连续两个大于127字节就组合成一个GB2312码。
GB2312汉字编码字符集对照表:
http://tools.jb51.net/table/gb2312
3、GBK编码
但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,不得不继续把 GB2312 没有用到的码位找出来用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 “GBK” 标准,GBK 包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
4、GB18030编码
后来中国的少数民族也要用电脑了,GBK的两万多字也已经无法满足我们的需求了,还有更多可能你自己从来没见过的汉字需要编码。这时候显然只用2bytes表示一个字已经不够用了(2byte最多只有65536种组合,然而为了和ASCII兼容,最高位不能为0就已经直接淘汰了一半的组合,只剩下3万多种组合无法满足全部汉字要求)。因此GB18030多出来的汉字使用4byte编码。
当然,为了兼容GBK,这个四字节的前两位显然不能与GBK冲突(实操中发现后两位也并没有和GBK冲突)。通过多年的发展至此,GB18030编码的中文文件已经有七万多个汉字了。
GB18030包含三种长度的编码:单字节的ASCII、双字节的GBK(略带扩展)、以及用于填补所有Unicode码位的四字节UTF区块。所以我们说GB18030采用多字节编码,每个字符可以由 1 个、2 个或 4 个字节组成。
其实我们用到的99%以上的汉字,都在GB2312那一块区域内。在实际使用中,GBK编码已经可以满足大部分场景了,GB18030编码中所有汉字都是我们这辈子都不一定能见到的文字,所以平时经常会使用的就是GBK编码。
这里额外总结一下这四个兼容性关系是GB18030兼容GBK,GBK兼容GB2312,GB2312兼容ASCII。所谓兼容,你可以简单理解为子集、不冲突的关系。
例如GB2312编码的文件中可以出现ASCII字符,GBK编码的文件中可以出现GB2312和ASCII字符,GB18030编码的文件可以出现GBK、GB2312、ASCII字符。
5、Unicode
友情建议:看Unicode一些概念解释和历史时,建议看维基百科,而且是英文版的,别用中文版,有些地方讲的模棱两可,容易陷入盲区。
Unicode(中文:万国码、国际码、统一码、单一码)(全称Universal Multiple-Octet Coded Character Set)它伴随着通用字符集(英语:Universal Character Set, UCS)的标准而发展。所以可以看出他是字符集。
(1)Unicode与 ISO 10646
全世界很多个国家都在为自己的文字编码,并且互不相通,不同的语言字符编码值相同却代表不同的符号(例如:韩文编码EUC-KR中“한국어”的编码值正好是汉字编码GBK中的“茄惫绢”)。
因此,同一份文档,拷贝至不同语言的机器,就可能成了乱码,于是人们就想:我们能不能定义一个超大的字符集,它可以容纳全世界所有的文字字符,再对它们统一进行编码,让每一个字符都对应一个不同的编码值,从而就不会再有乱码了。
如果说“各个国家都在为自己文字独立编码”是百家争鸣,那么“建立世界统一的字符编码”则是一统江湖,谁都想来做这个武林盟主。早前就有两个机构做了这个事:
国际标准化组织(ISO),他们于1984年创建ISO/IEC JTC1/SC2/WG2工作组,试图制定一份“通用字符集”(Universal Character Set,简称UCS),并最终制定了ISO 10646标准。(简单来说ISO 10646标准就是UCS)
统一码联盟,他们由Xerox、Apple等软件制造商于1988年组成,并且开发了Unicode标准(The Unicode Standard,这个前缀Uni很牛逼哦—Unique, Universal, and Uniform)。
Unicode与ISO 10646标准的风风雨雨:
在1984年,喜欢以繁多的编号糊弄群众的国际标准化组织ISO也开始着手制定解决不同语言字符数量太大问题的解决方案,这一方案被称为Universal Character Set(UCS),正式的编号是ISO-10646(记得么,ASCII是ISO-646,不知这种安排是否是故意的)。
还是ISO高瞻远瞩,一开始就确定了UCS是一个31位的编码字符集(即用一个大小不超过2的31次方的整数数字为每个字符编号),这回真的足以容纳古往今来所有国家,所有语言所包含的字符了(是的,任何国家,任何小语种都包括)。虽然后来他们意识到,2的31次方个码位又实在太多了……
天下大势,分久必合。无论Unicode还是UCS,最初的目的都是杜绝各种各样名目繁多形式各异互不兼容老死不相往来的私用扩展编码(好啰嗦的一句话),结果两方确立标准的同时(最初时这两个标准是不兼容的),因为都是个干个的,肯定不可能一模一样,出现标准不同。
1991年,Unicode联盟与ISO的工作组终于开始讨论Unicode与UCS的合并问题,虽然其后的合并进行了很多年,Unicode初版规范中的很多编码都需要被改写,UCS也需要对码空间的使用进行必要限制,但成果是喜人的。
最终,两者统一了抽象字符集(即任何一个在Unicode中存在的字符,在UCS中也存在),且最靠前的65535个字符也统一了字符的编码。对于码空间,两者同意以一百一十万为限(即两者都认为虽然65536不够,但2的31次方又太大,一百一十万是个双方都可接受的码空间大小,也够用,当然,这里说的一百一十万只是个约数),Unicode将码空间扩展到了一百一十万,而UCS将永久性的不使用一百一十万以后的码位。
也就是说,现在再讲Unicode只包含65536个字符是不对的(虽然大家现在都知道Unicode至少都可以囊括几亿个字符)。除了对已经定义的字符进行统一外,Unicode联盟与ISO工作组也同意今后任何的扩展工作两者均保持同步,因此虽然从历史的意义上讲Unicode与UCS不是一回事(甚至细节上说也不是一回事),但现在提起Unicode,指代两者均无不妥,毕竟因为已经统一了。
(现在网上基本上把Unicode字符集叫做UCS,Unicoide的全称是 Universal Multiple-Octet Coded Character Set简写也是UCS,一看也对上了,害,只能说天注定)。
现在Unicode编码点分为17个平面(plane),每个平面包含216(即65536)个码位(codepoint)。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。
这第一个位置(当xx是00的时候)被称为BMP(基本多文种平面,BasicMultilingualPlane)。它包含了最常用的码位从U+0000到U+FFFF(常见的65536个字符)。
其余16个平面(从下面的1号平面到16号平面),你可以叫做非BMP,由此这样分的话里面的字符就有两个概念:BMP字符和非BMP字符,后者也被称为补充字符。(偷偷告诉你们,大家喜闻乐见的Emoji表情都是在1号平面,范围是U+1F600-U+1F64F)
(2)UCS-2和UCS-4
ISO10646标准为“通用字符集”(UCS)定义了一种16位的编码形式(即UCS-2),UCS-2全称Universal Character Set coded in 2octets,从英文上就可以看出含义,以2字节编码的通用字符集编码,固定占用2个字节,它包含65536个编码空间(可以为全世界最常用的63K字符编码,为了兼容Unicode,0xD800-0xDFFF之间的码位未使用)。
例:“汉”的UCS-2编码为6C49。除此之外ISO10646标准为“通用字符集”(UCS)还定义了一种31位的编码形式(即UCS-4),UCS-4全称UniversalCharacter Set coded in 4 octets,其编码固定占用4个字节,编码空间为0x00000000 ~0x7FFFFFFF(可以编码20多亿个字符)。
随着Unicode与ISO 10646合并统一,Unicode就用UCS通用字符集标准,早期的 Unicode 编码实现也就采用了UCS-2和UCS-4。(准确来说是UCS-2,UCS-4基本上是理论,没付诸实际,毕竟早期65536个字符已经够用了,我两个字节编码能实现的事,脑子笨的人才会用四个字节实现,你以为存储和带宽有多的)。
这里编码最多也就存在UCS-2(big Endian和LittleEndian先不管,后面会讲)。
Unicode字符集只规定了码点和文字之间的对应关系,并没有规定码点在计算机中如何存储。UCS-2和UCS-4就规定了具体的实现,后来改进演变为了UTF-16, UTF-32。然后又创造了一种全新的简单粗暴好用的变长编码UTF-8,于是乎这三哥们就形成了现代Unicode字符集编码实现的三剑客。
(3)UTF-16与UTF-32
Unicode与ISO 10646合并统一后,Unicode与 ISO 10646 的通用字符集概念(UCS)相对应。早期实现Unicode用的编码是UCS-2,后来随着发展发现 216(即65536)个字符不能满足了,Unicode标准本身发生了变化:65536个字符显得不足,引入了更大的31位空间和一个编码(UCS-4),每个字符需要4个字节。
但是统一码联盟对此予以抵制(这就是为什么我之前说UCS-4是一种理论编码,根本就没付诸实际),这是因为每个字符4个字节浪费了很多磁盘空间和内存,并且因为一些制造商已经在每个字符2个字节的技术上投入了大量资金。所以最后通过一系列巴拉巴拉讨论规定形成了一种折衷方案,建立了UTF-16编码方案(此时Unicode标准2.0),它替代了原有的UCS-2,并做了改进。
它与UCS-2一样,它使用两个字节为全世界最常用的63K字符编码,不同的是,它使用4个字节对不常用的字符进行编码。目的就是为了支持从17个平面编码1,112,064个代码点。
UTF-16属于变长编码。我们可以将UTF-16编码看成是UCS-2编码父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一意思。但当引入辅助平面字符后,就称为UTF-16了。
现在应该认为UCS-2已作废,如果有人还用这种,也不必纠结,它就是表达用定长2字节编码,自己心里清楚就行(基本上你查维基百科上UCS-2都是重定向到UTF-16)。
另外当时ISO 10646的UCS-4编码并入了Unicode标准,而UCS-4有20多亿个编码空间,但实际使用范围并不超过0x10FFFF,并且为了兼容Unicode标准,ISO也承诺将不会为超出0x10FFFF的UCS-4编码赋值。
由此提出了实实在在的UTF-32编码(现在也应该认为UCS-4像UCS-2一样作废,维基百科上UCS-4也重定向到UTF-32页面),它的编码值与UCS-4相同,只不过其编码空间被限定在了0~0x10FFFF之间。因此也可以说:UTF-32是UCS-4的一个子集。
(现在若有软件声称自己支持UCS-2,那其实是暗指它不能支持在UTF-16中超过2字节的字集。)
UTF-16(16 位 Unicode转换格式)是一种字符编码,能够对Unicode的所有1,112,064个有效码点进行编码(实际上,此代码点数由UTF-16的设计决定,这个你细品你就知道什么意思,就好像某个班有55个人,根据55个座位确定55个人,而55个座位这个多少是由55个人决定的,两者是相互的,这是一个哲学道理,hh扯远了,所以其中意味自行明白)。
前面提到过:Unicode编码点分为17个平面(plane),每个平面包含216(即65536)个码位(codepoint),而第一个平面称为“基本多语言平面”(Basic Multilingual Plane,简称BMP),其余平面称为“辅助平面”(Supplementary Planes)。其中“基本多语言平面”(00xFFFF)中0xD8000xDFFF之间的码位作为保留,未使用。
UCS-2只能编码“基本多语言平面”中的字符,此时UTF-16与UCS-2的编码一样(都直接使用Unicode的码位作为编码值),例:“汉”在Unicode中的码位为6C49,而在UTF-16编码也为6C49。
另外,UTF-16还可以利用保留下来的0xD800-0xDFFF区段的码位来对“辅助平面”的字符的码位进行编码,因此UTF-16可以为Unicode中所有的字符编码。
UTF-16和UTF-32也就是如今Unicode编码的标准之二,他们的区别就是UTF-16是变长编码,大部分是2字节和少部分4字节,UTF-32是定长编码,表示任何字符都用 4 字节
(4)UTF-8
从前述内容可以看出:无论是UCS-2/4还是UTF-16/32,一个字符都需要多个字节来编码,这对那些英语国家来说多浪费带宽啊!(尤其在网速本来就不快的那个年代......),而且我们注意到UTF-16最少2字节和UTF-32不变4字节,这肯定是不兼容ASCII码的,由此,UTF-8产生了。
在UTF-8编码中,ASCII码中的字符还是ASCII码的值,只需要一个字节表示,其余的字符需要2字节、3字节或4字节来表示。
UTF-8的编码规则:
对于ASCII码中的符号,使用单字节编码,其编码值与ASCII值相同。其中ASCII值的范围为0~0x7F,所有编码的二进制值中第一位为0(这个正好可以用来区分单字节编码和多字节编码)。
其它字符用多个字节来编码(假设用N个字节),多字节编码需满足:第一个字节的前N位都为1,第N+1位为0,后面N-1 个字节的前两位都为10,这N个字节中其余位全部用来存储Unicode中的码位值。
现如今UTF-8 是互联网上使用最广的一种 Unicode 的实现方式,是其他两种无可比拟的。
(5)UTF的字节序和BOM
字节序就要先补充一点知识:
码元(code unit):是能用于处理或交换编码文本的最小比特组合。它代表某种编码中最小的可用来识别一个合法字符的最小字节数序列。
UTF-8使用变长的字节序列来表示字符;某个字符(对应一个码点)可能使用1-4个字节才能表示;在UTF-8中一个字符最小可能一个字节,所以我们规定1个字节就是一个码元;
UTF-16使用也变长字节序列来表示字符;某个字符(对应一个码点)可能使用2个或者4个字符来表示;因为2个字节序列是最小的能够识别一个码点的单位,同理我们规定2个字节就是一个码元;
UTF-32使用定长的4个字节表示一个字符;一个字符(对应一个码点)使用4个字符来表示,这样4个字节就是一个码元。
简单来说,就是“码点”经过映射后得到的二进制串的转换格式单位称之为“码元”。“码点”就是一串二进制数,“码元”就是切分这个二进制数的方法。这些编码每次处理一个码元,你可以把它理解为UTF-8每次读码点的8位,UTF-16每次读码点的16位,UTF-32每次读码点的32位,。当然这也是为什么叫这些叫Unicode转换格式的原因。处理的是同一个字符集,但是处理方式不同。
字节序
UTF-8一次一个UTF-8码元,即处理一个字节,没有字节序的问题。UTF-16一次处理一个UTF-16码元,对应两个字节,UTF-32一次一个UTF-32码元,对应处理四个字节,所以这就要考虑到一个字节序问题。
以UTF-16w为例,在解释一个UTF-16编码文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。
如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?这就考虑大小端问题,所以UTF-16编码包括三种:UTF-16BE(Big Endian),UTF-16LE(Little Endian)、UTF-16(类似的名称UCS-2BE和UCS-2LE用于显示UCS-2的版本。)
UTF-16BE和UTF-16LE好理解,直接指定了字节序(大小端),但是UTF-16怎么处理呢?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:
在UCS编码中有一个叫做"ZERO WIDTH NO-BREAKSPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。
同样的类比,UTF-32也是这样的。有UTF-32BE、UTF-32LE、UTF-32。前面UTF-32BE和UTF-32LE直接指定了字节序(大小端),后面的UTF-32也是靠BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAKSPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
windows就是使用BOM来标记文本文件的编码方式的。它就建议所有的 Unicode 文件应该以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符开头。这作为一个“特征符”或“字节顺序标记(byte-ordermark,BOM)”来识别文件中使用的编码和字节顺序。所以用Windows自带的记事本将文件保存为UTF-8编码的时候,记事本会自动在文件开头插入BOM(虽然BOM对UTF-8来说并不是必须的)。
但也有一些系统或程序不支持BOM,因此带有BOM的Unicode文件有时会带来一些问题。比如JDK1.5以及之前的Reader都不能处理带有BOM的UTF-8编码的文件,解析这种格式的xml文件时,会抛出异常:Content is not allowed inprolog。
linux/UNIX 并没有使用 BOM,因为它会破坏现有的 ASCII 文件的语法约定。所以一般我们不建议用Windows自带的记事本编辑UTF-8文件就是这样。
总结
1、简单地说:Unicode和UCS是字符集,不属于编码UTF-8、UTF-16、UTF-32等是针对Unicode字符集的编码,UCS-2和UCS-4是针对UCS字符集的编码(只是我们习惯把Unicode字符集编码简称为Unicode编码,把UCS字符集编码称为UCS编码)。
Unicode沿用UCS字符集,在UCS-2和UCS-4基础上提出的UTF-16、UTF-32。并发展了UTF-8,发展到现在,就密不可分了,大家基于UCS就把Uniocde维护好就行,发布标准大家统一。以往的UCS-2和UCS-4概念就默认作废了这样一个关系,整个他们的发展长话短说就是这样,懂了吗。
2、UTF-8、UTF-16、UTF-32、UCS-2、UCS-4对比:
由于历史方面的原因,你还会在不少地方看到把Unicode称为一种编码的情况,那是因为早期的2字节编码最初称为“ Unicode”,但现在称为“ UCS-2”,这种情况下的 Unicode 通常就是 UTF-16 或者是更早的 UCS-2 编码,只是被一直搞混了,在某些老软件上尤为常见。比如下面editplus里面文件编码设置。
以前的Windows电脑上的记事本(左边)显示的是Unicode,不过现在好像改了变成了UTF-16。
不过由于各种原因,必须承认,在不同的语境下,“Unicode”这个词有着不同的含义。
它可能指:
(1)Unicode 标准
(2)Unicode 字符集
(3)Unicode 的抽象编码(编号),也即码点、码位(code point)
(4)Unicode 的一个具体编码实现,通常即为变长的 UTF-16(16 或 32 位),又或者是更早期的定长 16 位的 UCS-2
所以像我一般有时候非要区分的话都是直接说全,Unicode 标准,Unicode 字符集,Unicode编码等等。
ANSI编码
为使计算机支持更多语言,通常使用0x800~xFF范围的2个字节来表示1个字符。比如:汉字‘中’ 在中文操作系统中,使用 [0xD6,0xD0]这两个字节存储。
不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的编码标准。这些使用多个字节来代表一个字符的各种语言延伸编码方式,称为 ANSI 编码。
在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。
不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。
在使用ANSI编码支持多语言阶段,每个字符使用一个字节或多个字节来表示(MBCS,Multi-Byte Character System),因此,这种方式存放的字符也被称作多字节字符。比如,“中文123” 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节。
在非 Unicode 环境下,由于不同国家和地区采用的字符集不一致,很可能出现无法正常显示所有字符的情况。微软公司使用了代码页(Codepage)转换表的技术来过渡性的部分解决这一问题,即通过指定的转换表将非Unicode 的字符编码转换为同一字符对应的系统内部使用的Unicode 编码。
可以在“语言与区域设置”中选择一个代码页作为非 Unicode 编码所采用的默认编码方式,如936为简体中文GBK,950为正体中文Big5(皆指PC上使用的)。在这种情况下,一些非英语的欧洲语言编写的软件和文档很可能出现乱码。而将代码页设置为相应语言中文处理又会出现问题,这一情况无法避免。
从根本上说,完全采用统一编码才是解决之道,虽然现在Unicode有了,但由于历史遗留,老软件等等原因,所以系统统一用某种编码格式的Unicode目前尚无法做到这一点。
代码页技术现在广泛为各种平台所采用。UTF-7 的代码页是65000,UTF-8的代码页是65001。简体中文上使用的代码页为936,GBK编码。
以前中文DOS、中文/日文Windows95/98时代系统内码使用的是ANSI编码(本地化,根据不同地区设置不同的系统内码Windows版本),现在win7,win10等等系统的内码都是用的Unicode。
不过微软为了以前的程序兼容性,比如在某些情况下,比如你的程序需要和不支持Unicode的程序交互时,可能还是会需要用到code page,提供代码页服务(就好比微软不能说:“老子支持unicode了,以后不支持Unicode的程序都给我滚粗。”只能撅着屁股让这些老掉牙的程序仍然可以运行,于是只好给他们提供一个“非Unicode默认字符集”) 。可以在cmd下输入chcp查看code page。
WindowsAPI 的Wide Char 表达是 UTF-16: Unicode (Windows), L"" 表示是转换为 wide char。
Cocoa的NSString 和 Core Foundation 的CFString 内部表达都是 UTF-16,所以其实 OSX 和 IOS 内部处理都用的是 UTF-16。
JAVAString 的内部表达是 UTF-16,所以大量跨平台程序和 Android 程序其实内部也在用 UTF-16。
大部分的操作系统和 UI framework 的内部字符串表达(内码)都是UTF-16,不过Linux系统内使用的内码是UTF-8。
Tip:内码和外码
在计算机科学及相关领域当中,内码指的是“将信息编码后,透过某种方式存储在特定记忆设备时,设备内部的编码形式”。在不同的系统中,会有不同的内码。
在以往的英文系统中,内码为ASCII。在繁体中文系统中,当前常用的内码为大五码。在简体中文系统中,内码则为国标码。
为了软件开发方便,如国际化与本地化,现在许多系统会使用Unicode做为内码,常见的操作系统Windows、mac OS X、Linux皆如此。许多编程语言也采用Unicode为内码,如Java、Python3。
外码:除了内码,皆是外码。要注意的是,源代码编译产生的目标代码文件(如果Java可执行文件或class文件)中的编码方式属于外码。
引用文章:
1、必读,真说清的话得结合上面的维基百科上的说法理解:
https://en.wikipedia.org/wiki/Universal_Coded_Character_Set
https://en.wikipedia.org/wiki/Unicode
https://en.wikipedia.org/wiki/UTF-16
https://en.wikipedia.org/wiki/UTF-32
2、除此之外参考引用了下面三篇文章的一些观点:
http://www.blogjava.net/zhenandaci/archive/2008/12/24/248014.html
https://www.cnblogs.com/malecrab/p/5300503.html
https://blog.51cto.com/polaris/377468
版权声明:本文为CSDN博主「Aledsan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/weixin_43465312/article/details/105918985