字符编码

 

字符编码,近年来一直困扰着我,感觉平常遇到的UTF-8,GBK,Unicode,ANSI等编码大致知道意思也就可以了,没有必要去给它一个细分。

但是,今天发现不得不去好好梳理一下他们的关系了。因为这个地方老是出些莫名其妙的问题。

1. 字符集和字符编码

  首先明确两个概念,“字符集”(charset)“字符编码”(encoding)。有些地方经常把这两个概念等同,比如说到ASCII码,有时候指“字符集”,有时候指“字符编码”,其实这是两个不同的概念,混淆了这两个概念,很难真正理解一些概念,比如Unicode和Utf8、Utf16的关系(其实Unicode是字符集和第一层字符编码,Utf8、Utf16是第二层字符编码,它们都表示了同一个字符集)。

  字符集顾名思义是“许多字符的集合”,这些字符组成一套符号系统,可以组合起来形象的表达各种含义。比如26个英文字母加上标点符号可以组成“英文字符集”,这个字符集的组合可以形成英美人可以理解的一套文字系统,看到了“I love you”,就能明白什么意思。再比如新华字典中的所有汉字加上标点符号可以组成“中文字符集”,这个字符集的组合可以形成中文文字系统,看到“我爱你”就能理解其含义。对于老外,如果不懂中文,看到“我爱你”三个字符,可能以为就是“鬼画符”。

  “字符编码”字面意思就是“对字符进行编码”。呵呵,听起来有点废话。那先说下为什么要对字符进行编码。对字符编码的最大目的还是为了传输、储存信息(其实储存也是为了传输,是为了能传给以后的人看)。

字符编码的四个步骤

  从上一节来看,“字符集”是一种形象表意的工具,“字符编码”是表示字符的一种方式。在计算机出现之前就已经有了这两种技术。计算机中,是使用二进制的方式对字符集重新编码。

    在计算机中,要建立一种“字符编码模型”,需要四步:

  1. 要有一个字符库,确定这些字符足够表意。 比如ASCII字符集,已经足够表示英语,但不能表示中文,于是产生GB2312字符集。
  2. 第一层编码给每个字符编个数字,英文叫Code Point 或 Code Position。比如ASCII字符集中,65表示“A”,97表示“a”。
  3. 第二层编码,确定表示字符的二进制位数(8位,16位,32位)。ASCII使用7位,DBCS(双字节字符集)使用16位。
  4. 第三层编码,确定字符二进制值的存储格式(大端法,小端法)。比如X86机器使用小端法。

    一种字符集一般只有一种编码方式,当字符集不够用时,会增加一些新的符号,形成新的字符集。对于新的符号会有新的数字,新的编码格式。所以有时“字符集”和“字符编码”的概念并不严格区分。比如ASCII码,可以指128个字符的字符集,也可以指对这128个字符的编码方式。不过有的字符集有多种编码格式,比如Unicode字符集,Utf8、Utf16都是其编码格式(第二层编码)。

 

2 常用计算机编码

2.1 ASCII码

 全称“American Standard Code for Information Interchange”,美国标准信息交换码,由美国标准委员会(American Standards Association,简称ASA)制定,后来该协会改组为“美国国家标准学会”(American National Standard Institute , 简称ANSI ),所以很多资料上说ASCII码是ANSI制定的。ASCII码是从电报码发展过来的,最早使用是用在7-bit的电传打字机上的。1960年10月6日,ASA开始ASCII的标准化工作,于1963年发布第一版,1967年再发布一次大的版本,这个标准版本,也是一个7-bit码,包含33个非打印字符(现在许多已经废弃了),95个打印字符(包含空格符),编码范围为0~127

注意,这个最初是美国的标准,包含的95个打印字符中,无法囊括世界上所有国家的字符,比如英镑符号“£”,各种拉丁符号“δ、β、θ”,中文、日文、韩文这么多象形文字等等。为了解决这个问题,各国都制定了自己的字符集和字符编码,不过,*本上都兼容7-bti ASCII码。比如ISO-8859系列,日文的JIS,中国大陆的GB2312、GBK,台湾的Big5等。

2.2 Extended ASCII码

  最初的标准ASCII码中包含的字符太少,很多应用或者国家中的符号都无法表示。比如数学符号“× ÷ ≠ ≥ ≈ π”。尤其20世纪70年代,随着PC的兴起,各电脑厂商开始增加自己的图形符号,形成了各种非标准的扩展ASCII码字符集(比如,ATASCIIPETSCII等)。TRS-80(美国早期的一种家庭电脑),增加了32个图形字符

  当时的IBM也为自己的PC定制了8-bit扩展ASCII码字符集,叫做代码页(code page),给不同语言制定不同的 code page,并把这些 code page 编号。这些 code page 都是兼容标准ASCII的。编码的数值为,从128-255扩展编码。比如,北美市场的DOS电脑,使用 code page 437,包含了法语、德语和一些其他欧洲国家的带音调的字符。code page 技术一直保留了下来,发展到现在,微软的windows内部就用了这种技术。

  苹果电脑也在 Mac OS 系统中引入了自己的8-bit扩展ASCII编码,如 Mac OS Roman。国家的带音调的字符。DEC公司也开发了 MCS(Multinational Character Set,多国字符集)。

 

  其实标准的ASCII就是7-bit的编码(8字节,但是最高位没有编码),后来使用过程中发现127个字符有点不够用,于是将ASCII进行了扩展,叫做Extended-ASCII或者high-ASCII,8位的,能表示256个字符。由于不同的应用场景,有不同的编码,有IBM的Extend ASCII和ANSI的Extend ASCII。去wikipedia上会发现有好多种ASCII的标准,大类就是IBM和ANSI(Windows的,微软很强势,ANSI的东西感觉就是给他们家用的)两种,其实都是为了给自家系统用的,随着IBM操作系统的坠落,IBM的扩展ASCII也*本上淡出视野。

wikipedia,Revisions of the ASCII standard:
    ASA X3.4-1963[1][4][16][17]
    ASA X3.4-1965 (approved, but not published, nevertheless used by IBM 2260 & 2265 Display Stations and IBM 2848 Display Control)[1]:423, 425–428, 435–439[16][17]
    USAS X3.4-1967[1][5][17]
    USAS X3.4-1968[1][17]
    ANSI X3.4-1977[17]
    ANSI X3.4-1986[7][17]
    ANSI X3.4-1986 (R1992)
    ANSI X3.4-1986 (R1997)
    ANSI INCITS 4-1986 (R2002)[18]
    ANSI INCITS 4-1986 (R2007)[19]
    ANSI INCITS 4-1986 (R2012)
wikipedia--Revisions of the ASCII standard

  看到没,早期有四个版本是IBM的。


  扩展的ASCII的产生
  既然IBM的扩展ASCII已经成为昨日黄花,我们只考虑ANSI的扩展ASCII的出现契机。
搭载Windows系统的计算机进入欧洲之后,发现标准的ASCII并不能满足欧洲这些拉丁语族国家的语言表意,决定对其进行扩展。同为印欧语系,发现扩展起来也没那么难,总共256个值就包括所有了。只需要将原来的7-bit扩展为8-bit,将原来的标准ASCII保留,第一位使用0来表示。将扩展的字符第一位使用1来表示

  扩展ASCII的组成

  具体来讲,扩展后的ASCII码表可以看成由三部分组成:

  第一部分:由00H到1FH共32个,一般用来通讯或作为控制之用。有些可以显示在屏幕上,有些则不能显示,但能看到其效果(如换行、退格)

  第二部分是由20H到7FH共96个,这95个字符是用来表示阿拉伯数字、英文字母大小写和下划线、括号等符号,都可以显示在屏幕上.

  第三部分由80H到0FFH共128个字符,一般称为"扩充字符",这128个扩充字符是由IBM制定的,并非标准的ASCII码.这些字符是用来表示框线、音标和其它欧洲非英语系的字母。

 

ascii值     字符     unicode
128    Ç     0xc7
129    ü     0xfc
130    é     0xe9
131    â     0xe2
132    ä     0xe4
133    à     0xe0
134    å     0xe5
135    ç     0xe7
136    ê     0xea
137    ë     0xeb
138    è     0xe8
139    ï     0xef
140    î     0xee
141    ì     0xec
142    Ä     0xc4
143    Å     0xc5
144    É     0xc9
145    æ     0xe6
146    Æ     0xc6
147    ô     0xf4
148    ö     0xf6
149    ò     0xf2
150    û     0xfb
151    ù     0xf9
152    ÿ     0xff
153    Ö     0xd6
154    Ü     0xdc
155    ¢     0xa2
156    £     0xa3
157    ¥     0xa5
158    ₧     0x20a7
159    ƒ     0x192
160    á     0xe1
161    í     0xed
162    ó     0xf3
163    ú     0xfa
164    ñ     0xf1
165    Ñ     0xd1
166    ª     0xaa
167    º     0xba
168    ¿     0xbf
169    ⌐     0x2310
170    ¬     0xac
171    ½     0xbd
172    ¼     0xbc
173    ¡     0xa1
174    «     0xab
175    »     0xbb
176    ░     0x2591
177    ▒     0x2592
178    ▓     0x2593
179    │     0x2502
180    ┤     0x2524
181    ╡     0x2561
182    ╢     0x2562
183    ╖     0x2556
184    ╕     0x2555
185    ╣     0x2563
186    ║     0x2551
187    ╗     0x2557
188    ╝     0x255d
189    ╜     0x255c
190    ╛     0x255b
191    ┐     0x2510
192    └     0x2514
193    ┴     0x2534
194    ┬     0x252c
195    ├     0x251c
196    ─     0x2500
197    ┼     0x253c
198    ╞     0x255e
199    ╟     0x255f
200    ╚     0x255a
201    ╔     0x2554
202    ╩     0x2569
203    ╦     0x2566
204    ╠     0x2560
205    ═     0x2550
206    ╬     0x256c
207    ╧     0x2567
208    ╨     0x2568
209    ╤     0x2564
210    ╥     0x2565
211    ╙     0x2559
212    ╘     0x2558
213    ╒     0x2552
214    ╓     0x2553
215    ╫     0x256b
216    ╪     0x256a
217    ┘     0x2518
218    ┌     0x250c
219    █     0x2588
220    ▄     0x2584
221    ▌     0x258c
222    ▐     0x2590
223    ▀     0x2580
224    α     0x3b1
225    ß     0xdf
226    Γ     0x393
227    π     0x3c0
228    Σ     0x3a3
229    σ     0x3c3
230    µ     0xb5
231    τ     0x3c4
232    Φ     0x3a6
233    Θ     0x398
234    Ω     0x3a9
235    δ     0x3b4
236    ∞     0x221e
237    φ     0x3c6
238    ε     0x3b5
239    ∩     0x2229
240    ≡     0x2261
241    ±     0xb1
242    ≥     0x2265
243    ≤     0x2264
244    ⌠     0x2320
245    ⌡     0x2321
246    ÷     0xf7
247    ≈     0x2248
248    °     0xb0
249    ∙     0x2219
250    ·     0xb7
251    √     0x221a
252    ⁿ     0x207f
253    ²     0xb2
254    ■     0x25a0
255    ÿ     0xff
扩展ASCII编码对照表

2.3 ISO-8859

  由于各公司、国家之间都有自己的字符集,同一个数值,在不同的字符集之间表示的符号可能不一样,这样在一台电脑上正常可以阅读的文件到另外一台电脑可能就成了乱码。为了解决这个问题,ISO组织统一了一套标准字符集,就是ISO-8859.

  不过 ISO-8859 不是一个字符集,而是一系列扩充的ASCII码字符集

ISO/IEC 8859-1 (Latin-1) - 西欧语言
ISO/IEC 8859-2 (Latin-2) - 中欧语言
ISO/IEC 8859-3 (Latin-3) - 南欧语言。世界语也可用此字符集显示。
ISO/IEC 8859-4 (Latin-4) - 北欧语言
ISO/IEC 8859-5 (Cyrillic) - 斯拉夫语言
ISO/IEC 8859-6 (Arabic) - 阿拉伯语
ISO/IEC 8859-7 (Greek) - 希腊语
ISO/IEC 8859-8 (Hebrew) - 希伯来语(视觉顺序)
ISO 8859-8-I - 希伯来语(逻辑顺序)
ISO/IEC 8859-9(Latin-5 或 Turkish)- 它把Latin-1的冰岛语字母换走,加入土耳其语字母。
ISO/IEC 8859-10(Latin-6 或 Nordic)- 北日耳曼语支,用来代替Latin-4。
ISO/IEC 8859-11 (Thai) - 泰语,从泰国的 TIS620 标准字集演化而来。
ISO/IEC 8859-13(Latin-7 或 Baltic Rim)- 波罗的语族
ISO/IEC 8859-14(Latin-8 或 Celtic)- 凯尔特语族
ISO/IEC 8859-15 (Latin-9) - 西欧语言,加入Latin-1欠缺的芬兰语字母和大写法语重音字母,以及欧元(€)符号。
ISO/IEC 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。

  由于英语没有任何重音字母(不计外来词),故可使用以上十五个字集中的任何一个来表示。至于德语方面,因它除了 A-Z, a-z 外,只用 Ä, Ö, ü, ä, ö, ß, ü 七个字母,而所有拉丁字集(1-4, 9-10, 13-16)均有此七个字母,故德语可使用以上十个字集中的任何一个来表示。
  此系列中没有-12号的原因是,此计划原本要设计成一个包含塞尔特语族字符集的“Latin-7”,但后来塞尔特语族变成了ISO 8859-14 / Latin-8。亦有一说谓-12号本来是预留给印度天城体梵文的,但后来却搁置了。

  IOS-8859-1 这个编码向下兼容ASCII,但是已经不是标准的ASCII了,因为不能互相转换(只能单向转换),它有新的名字。这个编码是在Windows(绑架ANSI)进入欧洲(ISO组织可能老巢在欧洲)时候一起搞出来的,标准编号叫做ANSI/ISO-8859-1-1987,简称ISO-8859-1。编码全称叫做「American National Standard for Information Processing-8-Bit Single-Byte Coded Graphic Character Sets-Part 1: Latin Alphabet No 1」,所以也简写作Latin-1,mysql设置的时候就会设置成latin1
  因为拉丁语系的国家太多,欧洲这片大陆也是动乱不安,ISO-8859-1一个是不够的,根据不同的地方,搞出了很多ISO-8859系的标准,具体可以见百度百科,从1到16。。。
我们常用的是IOS-8859-1,叫做“西欧字符集”。
  由于这个字符集包括了印欧语系,也就是当今世界大部分技术、开发工具发源地。。很多语言标准、开发工具的编码格式默认是IOS-8859-1,这也是中文乱码的一个坑。

2.4 MBCS、DBCS

上面所说的ASCII码、扩展ASCII码、ISO-8859中每个字符都是一个字节(8-bit)可以表示的,所以称为单字节字符集”(Single-Byte Character Set,简称SBCS)

单字节字符集最多只能同时表示256个字符,ISO-8859中总字符数可能超过256个,但同时能使用的字符要么是8859-1字符集中的,要么是8859-2字符集中的,每个字符集最多仍然是256个,如果一篇文章中要出现所有语系中的字符,也是做不到的。

  但是到了亚洲,中、日、韩等国家文字,每个文字都是一个符号,远远超过256个字符。于是,亚洲国家制定了自己的字符集,使用1个或2个或以上的字节数表示自己的字符集,这就是多字节字符集”(Multi-Bytes Character Sets,简称MBCS)

  windows 系统中,本地字符集就是MBCS,不过由于大部分字符是2字节的,所以又称为“双字节字符集”(Double-Bytes Character Sets,简称DBCS),所以有的时候看到MBCS、DBCS,都是一回事。MBCS是完全兼容标准ASCII码的。

 

2.5 GB2312、GB13000、GBK、GB18030

  这几种字符都是中国国家标准委员会制定的,简称“GB”(国标)XXX。

  1980年,中国制定了自己的字符集标准,全称为《信息交换用汉字编码字符集--*本集》,简称GB2312-80,一共收录了 7445 个字符,包括 6763 个汉字和 682 个其它符号。GB2312-80,简称为GB2312。

  1993年,国际标准Unicode 1.1版本推出,收录中国大陆、台湾、日本及韩国通用字符集的汉字,总共有20,902个。中国大陆制定了等同于Unicode 1.1版本的“GB 13000.1-93”,简称为GB13000。

  GB13000显然包含GB2312已有的文字和其他很多为包含的文字,如GB2312-80推出以后才简化的汉字(如“啰”),部分人名用字(如中国前总理***的“*”字),台湾及香港使用的繁体字,日语及朝鲜语汉字等。

  GBK全称《汉字内码扩展规范》(GBK即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification) ,中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订,国家技术监督局标准化司、电子工业部科技与质量监督司1995年12月15日联合以技监标函1995 229号文件的形式,将它确定为技术规范指导性文件。这一版的GBK规范为1.0版。 

  国家标准GB18030-2005《信息技术中文编码字符集》是我国继GB2312-1980和GB13000.1-1993之后最重要的汉字编码标准,是我国计算机系统必须遵循的*础性标准之一。 GB18030有两个版本:GB18030-2000和GB18030-2005。GB18030-2000是GBK的取代版本,它的主要特点是在GBK*础上增加了CJK统一汉字扩充A的汉字。GB18030-2005的主要特点是在GB18030-2000*础上增加了CJK统一汉字扩充B的汉字。

2.6 ANSI 编码

  ANSI原意是指美国国家标准协会,但是在windows系统中,ANSI编码意思却代表“本地编码”。也就是说,在中国代表GBK,在台湾代表Big5,在日本代表JIS,所以windows编程中常说的ANSI字符串,就是指本地编码的字符串,在中国,就是一种DBCS,用1个和2个字节表示一个字符的编码。

  为什么ANSI编码等同于本地编码?在微软的MSDN中找到一些说明:

  早期,windows 中的英语和西欧字符集 code page 1252,是在ANSI草案*础上制定的,这个草案最后发展成ISO 8859-1,由此看来code page 1252 是早于ISO 8859-1标准的。因为这个,windows code page,通常被称为“ANSI code page”。

  windows code page 有时候也指“系统活动代码页”,一个windows操作系统,总会有一个当前的活动代码页,也就是“本地代码页”。

  可能因为这个原因,ANSI编码等同于本地编码。

  不过也可以这么理解:ANSI编码本来只包含了英文编码,后来到了各国,在ANSI编码*础上扩展成了本地编码,所以ANSI编码也可以指本地编码,ANSI字符串也表示本地字符串。

  

  我们在windows下打开记事本,然后随便输入点内容,点击保存的时候,会有ANSI的选项,这是什么鬼,有的地方管这个叫做ANSI编码。
  事实上并没有ANSI编码,ANSI是什么,是American National Standards Institute美国国家标准协会,协会,机构而已。
ANSI也有自己的ASCII标准。但是我们看到的这个ANSI并不是特指ANSI的ASCII标准,这个应该指所有的本地化编码。
这个是微软的锅。一开始只有英文操作系统,用ANSI表示ANSI的Extend ASCII编码。但是到了欧洲就是ISO-8859-1编码,到中国应该是GBK编码,日本应该是JIS编码等等,为了把实际编码的差异隐藏起来,用所谓的ANSI编码来表示所有Windows系统上的地区化编码,然后操作系统自己做转换,不同的国家地区,就会对应不同的编码规范。
  ANSI应该叫地区化编码,只出现在Windows系统中,就好像一种工厂模式,被Windows系统用来统一地区化编码的叫法。
  再多说一点,下面的Unicode其实是UTF-16,或者说是UTF-16LE,Unicde big endian是UTF-16BE,UTF-8,就是UTF-8。这四个选项就能囊括所有的主流编码了,被归为这四种是有历史原因的。如果对这句话不明白,请看我的博客编码系列的其它文章,看完就明白了。

 

2.7 Unicode、UCS

  以上的编码都是本地化编码,一国之内还没有问题,但是要跨国,就不行了。比如汉字,在只有ISO-8859系列字符集的电脑上显示就只能是乱码了,要显示汉字,电脑上必须装GB2312或GBK的字符集。有没有一个字符集,能够包含全球所有的字符呢?这就是Unicode和UCS。

  Unicode起源于1987年,施乐的 Joe Becker、苹果的 Lee Collins 和 Mark Davis 就开始研究能否创造一种全球通用的字符集。1988年,Joe Becker 发布了一个草案,提出了“Unicode”的概念,他解释说“‘Unicode’是一种唯一的、统一的、全球的编码”。后来,RLG、Sun、Microsoft、NeXT(乔布斯被赶出苹果后创建的公司)的人也都逐渐加入到Unicode工作组里。1991年1月3日,Unicode联盟组织成立,同年发布了Unicode1.0.

  同时,ISO组织也在做同样的事情,创造一个全球统一的字符集(Universal Coded Character Set,简称UCS),1993年发布了标准ISO 10646-1。

  后来,两个组织认识到,世界不需要两个不兼容的字符集,于是,开始合作。从Unicode2.0开始,开始采用和UCS相同的字库和字码。这样,两个项目仍都存在,并独立地公布各自的标准。但双方都同意保持两者标准的码表兼容,并紧密地共同调整任何未来的扩展。所以,现在说到UCS字符集,跟Unicode可以看成一回事。

  Unicode编码包含两个层次:第一层定义字符的数值第二层定义数值的实现方式。Unicode用数字 0x0~0x10FFFF 表示所有字符,所以最多可以容纳 1114112 个字符。数值的编码方式,也就是实现方式包括 UTF-8,UTF-16,UTF-32 三种

  有人会说,Unicode不是两个字节表示字符的码?为什么数值可以到0x10FFFF,这不21位,两个半字节还多了吗?其实,这是混淆了Unicode的数值定义和实现,这根本就是两个概念,Unicode到底用几个字节表示,取决于其实现方式是UTF-8,UTF-16,还是UTF-32.

  比如,“汉字”对应的Unicode值是0x6c49和0x5b57,而编码实现是:

char data_utf8[]=     {0xE6,0xB1,0x89,0xE5,0xAD,0x97};   //UTF-8编码
char16_t data_utf16[]=  {0x6C49,0x5B57};               //UTF-16编码
char32_t data_utf32[]=  {0x00006C49,0x00005B57};       //UTF-32编码

 

2.8 UTF-8

    UTF,全称“Unicode Transformation Formats”。是Unicode的编码格式。

    UTF-8是使用8-bit为单位,对Unicode进行编码的。特点是,对不同范围的字符使用不同长度的编码

Unicode编码(十六进制) 
UTF-8 字节流(二进制)
00000000 - 0000007F
0xxxxxxx
00000080 - 000007FF
110xxxxx 10xxxxxx
00000800 - 0000FFFF
1110xxxx 10xxxxxx 10xxxxxx
00010000 - 001FFFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
00200000 - 03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
04000000 - 7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8 的编码规则很简单:

  如果只有一个字节那么最高的比特位为 0;如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1,就使用几个字节编码,剩下的字节均以 10 开头具体的表现形式为(xxx 就用来存储 Unicode 中的字符编号):
  0xxxxxxx:单字节编码形式,这和 ASCII 编码完全一样,因此 UTF-8 是兼容 ASCII 的;
  110xxxxx 10xxxxxx:双字节编码形式;
  1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式;
  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式。
  下面是一些字符的编码实例(绿色部分表示本来的 Unicode 编号):

字符 N æ
Unicode 编号(二进制) 01001110 11100110 00101110 11101100
Unicode 编号(十六进制) 4E E6 
 2E EC
UTF-8 编码(二进制) 01001110 11000011 10100110 11100010 10111011 10101100
UTF-8 编码(十六进制) 4E C3 A6 E2 BB AC

  UTF-8编码的最大长度是6个字节。

  对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同,用1个字节表示,首位为0。

  对于0x80-0x7FF之间的字符,用2个字节表示,第一个字节前三位“110”为标志位,第二个字节前两位“10”为标志位。剩下的11位用来表示Unicode值(7FF最多11位)。

  同样,UTF-8的3个字节,可以表示0x800-0xFFFF的Unicode(最多16位)。

  UTF-8的4个字节,可以表示0x10000-0x001FFFFF的Unicode(最多21位)。

  4个字节以内,已经包含了Unicode所有字符。

  5、6个字节表示的已经是非Unicode编码范围,属于UCS-4 编码。早期UTF-8规范也可以达到6字节序列,不过2003年11月UTF-8 被 RFC 3629 重新规范,只能使用原来Unicode定义的区域, U+0000到U+10FFFF。根据规范,这些字节值将无法出现在合法 UTF-8序列中。

  例:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
  例2:Unicode编码0x20C30在0x010000-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。

  UTF-8有两个好处:

  1. 1字节字符、2字节字符、3字节字符……的首字节标志位不同,这样可以很清楚的区分一个字节属于1字节字符还是2字节字符,如果一个字节流传输中出现错误,也不会错位,只影响部分字符,根据标志位,很容易找到下个正确字符。

  2. 兼容ASCII码,英美字符用UTF-8可以一个字节表示,所以,www组织选用UTF-8作为推荐编码格式。2007年,在互联网上,UTF-8格式已经超过了ASCII码。

 

2.9 UTF-16

  UTF-16以2字节为单位,等同于UCS-2.

Unicode编码(十六进制 
UTF-16 字节流(二进制)
00000000 - 0000FFFF
xxxxxxxx xxxxxxxx
00010000 - 0010FFFF
110110yyyyyyyyyy 110111xxxxxxxxxx

   Unicode值小于等于0xFFFF的,直接用两个字节表示,超过0xFFFF的,无法用两个字节表示。使用下面公式编码:

1. 计算 U’= U – 0x10000

2. 将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxx

3. 加上标志位,1101 10yy yyyy yyyy 1101 11xx xxxx xxxx

  可见,这是4个字节表示,2个6位标志位,20位有效位。因为U最大是0x10FFFF,所以U’最大是0xFFFFF,20位足够表示。

  windows上默认的Unicode编码方式就是UTF-16,使用wchar_t表示。

2.10 UTF-32

  UTF-32编码以4字节为单位。直接把Unicode值转为4字节二进制数就是其UTF-32编码。等同于UCS-4.

2.11 Base64

有的电子邮件系统(比如国外信箱)不支持非英文字母(比如汉字)传输,这是历史原因造成的(认为只有美国会使用电子邮件?)。因为一个英文字母使用ASCII编码来存储,占存储器的1个字节(8位),实际上只用了7位2进制来存储,第一位并没有使用,设置为0,所以,这样的系统认为凡是第一位是1的字节都是错误的。而有的编码方案(比如GB2312)不但使用多个字节编码一个字符,并且第一位经常是1,于是邮件系统就把1换成0,这样收到邮件的人就会发现邮件乱码。
为了能让邮件系统正常的收发信件,就需要把由其他编码存储的符号转换成ASCII码来传输。比如,在一端发送GB2312编码->根据Base64规则->转换成ASCII码,接收端收到ASCII码->根据Base64规则->还原到GB2312编码
 

2.12 Big5

  在台湾、香港与澳门地区,使用的是繁体中文字符集。而1980年发布的GB2312面向简体中文字符集,并不支持繁体汉字。在这些使用繁体中文字符集的地区,一度出现过很多不同厂商提出的字符集编码,这些编码彼此互不兼容,造成了信息交流的困难。为统一繁体字符集编码,1984年,台湾五大厂商宏碁、神通、佳佳、零壹以及大众一同制定了一种繁体中文编码方案,因其来源被称为五大码,英文写作Big5,后来按英文翻译回汉字后,普遍被称为大五码。
  大五码是一种繁体中文汉字字符集,其中繁体汉字13053个,808个标点符号、希腊字母及特殊符号。大五码的编码码表直接针对存储而设计,每个字符统一使用两个字节存储表示。第1字节范围81H-FEH,避开了同ASCII码的冲突,第2字节范围是40H-7EH和A1H-FEH。因为Big5的字符编码范围同GB2312字符的存储码范围存在冲突,所以在同一正文不能对两种字符集的字符同时支持。
  Big5编码的分布如表1-5所示,Big5字符主要部分集中在三个段内:标点符号、希腊字母及特殊符号;常用汉字;非常用汉字。其余部分保留给其他厂商支持。
编码范围
符号类别
8140H-A0FEH
保留(用作造字区)
A140H-A3BFH
标点符号、希腊字母及特殊符号
A3C0H-A3FEH
保留(未开放用于造字区)
A440H-C67EH
常用汉字(先按笔划,再按部首排序)
C6A1H-C8FEH
保留(用作造字区)
C940H-F9D5H
非常用汉字(先按笔划,再按部首排序)
F9D6H-FEFEH
保留(用作造字区)
  Big5编码推出后,得到了繁体中文软件厂商的广泛支持,在使用繁体汉字的地区迅速普及使用。目前,Big5编码在台湾、香港、澳门及其他海外华人中普遍使用,成为了繁体中文编码的事实标准。在互联网中检索繁体中文网站,所打开的网页中,大多都是通过Big5编码产生的文档。
 

3 各种字符编码之间的关系

 上面关于字符集和编码讲了许多概念,其实归类一下可以这么理解:

 首先是单字节字符集:

    1、最初美国ANSI发明了自己的编码ASCII7-bit足够,这是标准ASCII

    2、标准ASCII码没有西欧国家拉丁文、英镑等字符,各公司、国家开始扩展,形成自己的扩展ASCII码字符集,各方混战,不过8-bit也就足够。

    3、天下分久必合,ISO统一了8-bit字符集,叫做ISO 8859.

但是亚洲国家字符更多,一个字节远远不够,于是用多个字节表示,扩展形成本国字符集,中国GB系列,台湾Big5,日本JIS……,这些叫做多字节字符集(MBCS),windows中用双字节表示,也叫做(DBCS

以上字符都是群雄割据,各自为政,windows为了迎合大家需求,在哪个国家,默认编码就用那个国家的,不过后来不知怎么被误传位ANSI编码,其实ANSI怎么可能定义世界各国编码,不过可以理解成各编码都是在ANSI*础上扩展的,因为都兼容ANSI的标准ASCII

这时,ISO再次出手,和Unicode联盟携手打造了UnicodeUCS,意图一统江湖。Unicode确实包罗万象,涵盖了各国字符,于是流行世界。Unicode自身只定义了每个字符的数值真正二进制编码格式UTF-8UTF-16UCS-2),UTF-32UCS-4

至此,天下一统,但愿程序员的太平盛世到来了!

 

参考文章

字符集和字符编码notbecoder

字符编码的概念(UTF-8、UTF-16、UTF-32都是什么鬼) ,   严长生.

字符编码,百度百科,2018.12。

 ASCII,百度百科,2018.12.

 ASCII码表及扩展ASCII码表松下一田

计算机编码--2.ASCII与ANSI、ASCII扩展与IOS-8859-1今夕何夕_19882018.7。

 

 

posted @ 2018-12-05 19:03  wenglabs  阅读(3725)  评论(1编辑  收藏  举报