关于字符集和字符编码自己汇总记录
关于Unicode的中文百科 https://zh.wikipedia.org/wiki/Unicode
英文百科 https://en.wikipedia.org/wiki/Unicode 【信息比中文版的更新】
关于字符编码,有三个核心概念:
- 字符集(Character Set),可以说是一个抽象概念,字符的合集。
- 码位(Code Point),也叫码点,将抽象的字符集中的每一个字符映射到一个整数。Unicode(即UCS-2或UCS-4)就是属于这一层的概念。它完全是数学的抽象,和计算机没有任何关系。
- 字符编码(Encoding),按照某种编码规则用二进制来表示一个字符。UTF-8,UTF-16等都是属于这一层的概念。
大量参考了维基百科。
一:Unicode可以说是一个抽象的名称,表示一个可以容纳世界上所有字符的编码方式,可以译为:统一码、万国码、国际码等。
二:Unicode的这个抽象名称的具体编码方式,现在有两种:
①,UCS-2:目前实际应用的Unicode版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节(UCS-2是定长双字节编码,而ISO-8559-1(即Latin1字符集)是单字节编码,所以两者是不兼容的,不过相对于ISO-8559-1编码来说,Unicode编码只是在前面增加了一个0字节,比如字母‘a’为 ‘00 61’)。这样理论上一共最多可以表示216(即65536)个字符。基本满足各种语言的使用。实际上当前版本的统一码并未完全使用这16位编码,而是保留了大量空间以作为特殊使用或将来扩展。
②,UCS-4:UCS-4是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即每个字符占用4个字节。理论上最多能表示231个字符,完全可以涵盖一切语言所用的符号。UCS-4可以看做UCS-2的扩展,双字节的UCS-2编码对应的四字节的UCS-4编码后两位相同,前两个字节的所有位都为0。UCS-4分为多个平面(plane),其中所有UCS-2的字符构成 基本多文种平面(Basic Multilingual Plane,BMP,也叫平面0)。或者说UCS-4中,高两个字 节为0的码位被称作BMP。将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字 节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
Unicode最初产生时是只有UCS-2编码的,但是后来发现如果把中国故纸堆里的罕用字以及各种小语种的所有文字都收录进去的话,16位UCS-2仍然不够用。于是Unicode才升级成了UCS-4。不过应用最广泛的仍然是UCS-2.
三:Unicode的实现方式(实际用来存储和传输的方式,即对Unicode编码进行二次编码):
Unicode的编码方式定义了每个字符的编码,但是如果就使用比如说UCS-2编码来存储和传输字符,对于Latin1中的字符来说,其第一个字节都是0,没有任何意义,而且还会造成空间的巨大浪费。这是不可接受的(另外还有一些其他原因)。所以Unicode的实现方式并不同于其编码方式。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称UTF)。常用的UTF有:UTF-8,UTF-16,UTF-32。
①UTF-8:其中文维基百科地址:https://zh.wikipedia.org/wiki/UTF-8 。UTF-8是一种变长编码,他将基本7位ASCII字符仍用7位编码表示(因为UTF-8中的每个字节的最高位有特殊用途,所以只能兼容7为的ASCII码而不能兼容Latin1),占用一个字节(首位补0).而遇到其他Unicode字符混合的情况,将按照一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。(注:这里指的是针对UCS-2的Unicode的转换,如果是用的UCS-4,那么其每个字符的UTF-8的编码将使用1-6个字节)。具体的转换算法参见维基百科(其实算法也很简单,这里简单说下,如下表所示,将一个待转换的Unicode编码先写成二进制形式,然后对照下表,将这个二进制形式填入对应的字节中的x即可,例如:“汉”这个字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了: 1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001,用这个比特 流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89),这里简要摘抄一下UTF-8编码后计算机如何识别一个字符:
Unicode 和 UTF-8 之间的转换关系表 ( x
字符表示码点占据的位 )码点的位数 码点起值 码点终值 字节序列 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx
10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx
10xxxxxx
10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx
10xxxxxx
10xxxxxx
10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx
10xxxxxx
10xxxxxx
10xxxxxx
10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x
10xxxxxx
10xxxxxx
10xxxxxx
10xxxxxx
10xxxxxx
如上表所示,这是对UCS-4编码的Unicode的情况,对于UCS-2只用到了最多3个字节。
对上表的说明:对于UTF-8编码中的任意字节B,
- 如果B的第一位为0,则B独立的表示一个字符(ASCII码);
- 如果B的第一位为1,第二位为0,则这个字节B为一个多字节字符中的一个字节(非ASCII码);
- 如果B的前两位为1,第三位为0,则这个字节B为两个字节表示的字符中的第一个字节;
- 如果B的前三位为1,第四位为0,则这个字节B为三个字节表示的字符中的第一个字节;
- 4个字节到6个字节的字符同理。
因此,总结一下,UTF-8的设计有一下几个特质:
- 单字节字符的最高位永远为0.
- 多字节序列中的首个字节的几个最高位决定了序列的长度。最高位为 110 的是2字节序列,而 1110 的是三字节序列,如此类推。
- 多字节序列中的其余的字节中的首两个最高位永远为 10 。
UTF-8的这些特质,保证了一个字符的字节序列不会包含在另一个字符的字节序列中。所以传输过程中不需要区分字节序。
②UTF-16:对于UCS-2来说,UTF-16这种Unicode的实现方式和UCS-2的编码是一致的,即用两个字节表示一个字符(即对于UCS-2来说,UTF-16是定长编码)。如果不怕浪费空间或是有特殊需要使用UTF-16这种传输方式,还有一个问题需要注意:字节顺序。由于每个字符占用两个字节,又因为不同的计算机系统上对字节顺序的理解是不一样的,所以同一字节流可能会被解释为不同的内容,如某字符的十六进制编码是4E59,按两个字节拆分成4E和59,在Mac电脑上读取时是从低字节开始,那么在Mac Os上会认为此4E59编码为594E,找到的字符是"奎",而在Windows 上则是从高字节开始读取,会认为此字符是U+4E59,即汉字"乙"。如果在Windows下以UTF-16编码保存一个字符"乙",在Mac Os环境下打开会显示成"奎"。此类情况说明UTF-16编码顺序不加以认为定义就可能发生混淆(只要是多字节问题,就会有端序问题),于是在UTF-16编码实现方式中使用了 大端序(Big-Endian,简写为UTF-16 BE)、小端序(Little-Endian,简写为UTF-16 LE) 的概念(通过BOM来区分是大端序还是小端序)。目前在PC机上的Windows系统和Linux系统对于UTF-16编码默认使用UTF-16 LE,小端即低位字节在先,大端即高位字节在先。
BOM(Byte Order Mark,字节顺序标记)是为UTF-16和UTF-32准备的,用于标记字节序。
BOM的实现原理是一个有点小聪明的想法:在UCS编码中有一个叫做“ZERO WIDTH NO-BREAK SPACE”的字符(零宽度非换行空格,此字符现在只能出现在字节流的开头用于表示字节序,除此之外的用法都已经被舍弃,其本身表达的空格含义被U+2060替代来表达),它的编码是FEFF。而FFFE在UCS中没有定义字符,所以此字符理论上讲是不会出现在实际传输中的。UCS规范建议我们在传输字节流前,先传输字符“ZERO WIDTH NO-BREAK SPACE”,这样如果接收者收到FEFF,就表明这个字节流是Big-Endian;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称为BOM。
UTF-8不需要表明字节顺序,但可以用BOM来表明编码方式,字符“ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是 EF BB BF,所以如果接收者收到了EF BB BF 开头的字节流就知道这是UTF-8编码了,带有BOM的UTF-8文件开头有U+FEFF。微软在UTF-8中使用BOM是因为这样可以把UTF-8和ASCII等编码明确区分开,但这样的文件在Windows之外的操作系统里会带来问题。
对于UCS-4来说,2字节编码的UTF-16则也变成了变长编码,需要通过一定的算法转换(现在出现的emoji表情突破了2个字节)。
③UTF-32,这个实现方式是对于UCS-4来说 也是定长编码(4个字节),因为它已经足够直接保存UCS-4编码了。
四:字符编码和内存
参考链接:https://www.zhihu.com/question/52346583
笼统来说,内码要求处理效率高,外码要求存储和传输方便。两者不可兼得的时候就会采用不同的编码方式。UTF-8没有字节序且节省空间,所以用来作数据存储和传输很方便。定长的编码在内存中可以快速访问,所以用来作数据处理时效率高。
上面讲到字符的实际存储和传输用的都是Unicode的实现方式,比如在保存和传输文本的时候,用UTF-8很多,因为对于大量以拉丁字母等ANSI字符为主的文献,UTF-8非常节省空间。但计算机处理文本的时候,内存中一般都不用UTF-8。因为UTF-8是变长编码,字符不定长会给算法带来麻烦,不从头扫描一遍,就不知道第几个字符在哪个位置上,这在处理的时候非常浪费时间。举个例子:
“ZH药丸” 是一个四个字符的字符串
UTF-8 编码是 "5A 48 e88daf e4b8b8" UTF-16 编码是 "005a 0048 836f 4e38" (这里还有字节序的问题,但是我们先忽略)
如果我想让你找到第四个字符是啥,UTF-8 必须扫过整个 字节流,而使用 UTF-16 的话,直接取出第四个16位整形(4e38)就好了(这是历史,美好的时代已经过去,现在有些字符比如 emoji 也需要多于2个字节了)。
很多语言/程序的处理办法是在内存中使用UTF-16编码处理字符(这里指的是针对UCS-2的UTF-16,只有针对UCS-2的UTF-16编码才是定长编码,Windows系统中用的便是针对UCS-2的UTF-16,微软当时用的时候Unicode还只有UCS-2,所以UTF-16是定长的。现在Unicode扩展到了UCS-4之后,UTF-16是不定长的了,但WIndows并没有更新,依然还使用的UCS-2),这对绝大多数字符而言足够用了。当然对于扩充后的Unicode(UCS-4中多于UCS-2的)字符是不支持的。
cmd: chcp 936 GBK chcp 65001 utf-8
一直被电脑的字符集困扰,ascii gbk utf8以及电脑如何显示字符.........等等。通过学习python终于搞明白了(有些说法不太准确)。记录下
参考资料如下:(一篇博客和两个知乎上的帖子,非常好)
http://blog.csdn.net/xiaolei1021/article/details/52093706
在计算机中为何不直接使用UTF8编码进行存储,而要使用Unicode再转换成UTF8?
https://www.zhihu.com/question/52346583
GB2312、GBK、GB18030 这几种字符集的主要区别是什么?
https://www.zhihu.com/question/19677619
关于Unicode字符集 (注意unicode占用字节数和utf8占用字节数是不同的)
最初的unicode编码是固定长度的,16位,也就是2两个字节代表一个字符,这样一共可以表示65536个字符。显然,这样要表示各种语言中所有的字符是远远不够的。Unicode4.0规范考虑到了这种情况,定义了一组附加字符编码,附加字符编码采用2个16位来表示,这样最多可以定义1048576个附加字符,目前unicode4.0只定义了45960个附加字符。
Unicode只是一个编码规范,目前实际实现的unicode编码只要有三种:UTF-8,UCS-2和UTF-16,三种unicode字符集之间可以按照规范进行转换。
http://blog.sina.com.cn/s/blog_4b4409c30100vw9t.html
汉字的unicode区间是
这个区间内的unicode,以\u4EOO
到\u9FA5;
\u0800 - \uFFFFutf-8
编码存储,都是占3个字节,所以上述区间内的汉字,全部都是3个字节但utf-8本身支持1-6个字节,用以覆盖需要用4个字节来表示的所有unicode字符
然后结合python中的一些知识记录下自己的理解:
0,ascii码的字符代码即是字符编码。如,“A"的字符代码是65,此也即是A的字符编码,无论65是用十进制还是用十六进制表示,这个字符代码是唯一的。
1,gb2312-->gbk-->gb18030是ascii码的中文扩展集,每个字符的字符代码即是字符编码。与这些编码并列的还有其他各个地区自己的编码,比如台湾地区有繁体中文的ascii码扩展集,叫big5。
2,unicode和gb*字符集没有任何关系,要说有联系,唯一的联系就是它们都兼容ascii码(其他字符,gbk字符代码和unicode字符代码是不同的)。unicode字符集包含了全世界各个国家和地区的各民族的所有字符。unicode字符集的字符代码和字符编码是不同的,字符代码是某个字符对应的唯一的编号,而字符编码涉及到互联网传输问题,unicode字符编码要用到1个或者2个或者3个或者4个字节,甚至更多,如此便产生了问题,计算机如何识别3[以3作为例子]个字节呢,是识别成一个字符呢,还是识别成3个字符呢?现在有个解决方案,就是把unicode中的所有字符都统一定义为3个,那么新的问题是unicode中的1字节和2字节字符也必须转为3字节,这在互联网传输中会造成带宽的极大浪费,是不能容忍的,所以这种方法不可行。为了解决这个问题,便出现了unicode字符的编码,比如utf-8,utf-16,utf-32等等,具体的实现可参考其他文章。举个例子,理解这段话,'仝'这个字的unicode字符代码是用十六进制数 <4edd>[转化为2进制有15位,用到了两个字节]表示的,用utf-8编码则转换为三个字节<\xe4\xbb\x9d>.....但从这个例子看貌似直接用字符代码表示更加节省资源,但从整体上看还是编码更好。
3,另外一个一直困扰我的问题是,计算机如何显示一个汉字。刚刚看到一句话,"计算机内存中,统一使用Unicode编码,需要保存或者传输时,转换成UTF8编码。",然后结合python的一些函数终于理解了。计算机中显示汉字是用到了这个汉字的unicode字符代码,而不是根据utf8编码显示的。根据python的函数可以证明:python3中所有的字符都是用unicode表示的了,在python(win7 python3.6) 交互模式下:
>>> '仝'.encode('utf8') #对'仝'用utf8编码,显示的字节串
b'\xe4\xbb\x9d>>> '仝'.encode('utf8').decode('utf8') #对字节串解码成unicode字符代码,显示出字符本身。
'仝'4,关于python中的一些知识:
①,python中的字节类型BYTE,用前缀b表示。对unicode进行utf8编码实际上就是转为了字节串类型,把utf8转为unicode就是把utf8字节串转为unicode字符代码(unicode字符代码是真正的字符串,python2中的str类型实际上是字节串)
②,python3中str函数有两种形式:
一是class
str
(object=''),即不带编码和错误参数,此时原样返回object的字符串形式,传入字节串返回的也是字节串的字符形式,参考如下例子:>>> a = b'\xe4\xbb\x9d'
>>> str(a)
"b'\\xe4\\xbb\\x9d'"
>>>
>>> type(a)
<class 'bytes'>
>>>
>>> type(str(a))
<class 'str'>实际上此时,传入的参数被视为unicode字符代码,即使传入参数形式上是字节串,但解释器会把这个字节串中的每个字符都当作unicode字符,不会调用decode()进行解码行为。
二是
str
(object=b'', encoding='utf-8', errors='strict'),即带了编码或者错误参数,此时第一个参数必须是字符的编码形式,即必须是字节串形式。此时,此函数实际上调用了decode()方法对编码进行解码,将第一个参数变为unicode字符代码,从而在电脑上显示出来。参考如下例子:>>> str(b'\xe4\xbb\x9d', encoding='utf8')
'仝'
>>> str('仝', encoding='utf8')
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: decoding str is not supported③,【新增】decode() 函数,具体语法 bytes.decode(encoding="utf-8", errors="strict") 是将字符的字符编码解码成字符代码,对于unicode编码而言,很好理解,字符编码和字符代码是不同的,decode(“UTF8“)先将编码解码成代码,然后,然后查找对应的字符,最后显示出来。但是对于像gbk这类的编码,其字符代码和字符编码是相同的,如何理解bytes.decode("GBK")呢?可以理解为先将bytes编码解码成相同的字符代码,然后在查找对应字符,最后显示出来。如,'中'字的gbk字符代码是D6D0(54992),其gbk字符编码是b'\xd6\xd0',其实和unicode一样,只不过是unicode的两者不一样,gbk的两者一样罢了。
GBK编码:汉字占两个字节
UTF-8编码: 通常汉字占三个字节, 扩展B区以后的汉字占四个字节
UTF-16编码: 通常汉字占两个字节,CJKV扩展B区、扩展C区、扩展D区中的汉字占四个字节(一般字符的Unicode范围是U+0000至U+FFFF,而这些扩展部分的范围大于U+20000,因而要用两个UTF-16)
中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030
转自: http://www.blog.edu.cn/user3/flyingcs/archives/2006/1418577.shtml 概要:UTF-8的一个特别的好处是它与ISO- 8859-1完全兼容,可以表示世界上所有的字符,汉字通常用3个字节来表示。GB2312的code page是CP20936。GBK的code page是CP936 。GB18030支持的字符数更多。GB2312、GBK、GB18030均为双字节。这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级。整理这篇文章的动机是两个问题:
问题一: 使用Windows记事本的“另存为”,可以在GBK、Unicode、Unicode big endian和UTF-8这 几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢?
我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字 节,分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。 但这些标记是基于什么标准呢?
问题二: 最近在网上看到一个ConvertUTF.c,实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互 转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式,我原来就了解。但这个程序让我有 些糊涂,想不起来UTF-16和UCS2有什么关系。
查了查相关资料,总算将这些问题弄清楚了,顺带也了解了一些Unicode的细节。作者写成 一篇文章,送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂,但要求读者知道 什么是字节,什么是十六进制。
0、big endian和little endian big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码 是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前 面,就是big endian。如果将49写在前面,就是little endian。
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big -Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,一个皇帝送了命 ,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小 尾”。
1、字符编码、内码,顺带介绍汉字编码
字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期 的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用 于繁体中文的big5。
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码 范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA- D7FE。
GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区 和图形符号区。汉字区包括 21003个字符。2000年的GB18030是取代GBK1.0的正式国家标准 。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字 。现在的PC平台必须支持GB18030,对嵌入式产品暂不作要求。所以手机、MP3一般只支持 GB2312。
从ASCII、GB2312、GBK到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案 中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一 地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK 到 GB18030都属于双字节字符集 (DBCS)。
有的中文Windows的缺省内码还是GBK,可以通过GB18030升级包升级到GB18030。不过GB18 030相对GBK增加的字符,普通人是很难用到的,通常我们还是用GBK指代中文Windows内码 。
这里还有一些细节:
GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
在DBCS中,GB内码的存储格式始终是big endian,即高位在前。
GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GB K和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字 符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低 字节的高位是什么。
2、Unicode、UCS和UTF
前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCI I兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编 码是6C49,而GB码是BABA。
Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字 的编码方案。Unicode的学名是 "Universal Multiple-Octet Coded Character Set",简 称为UCS。UCS可以看作是"Unicode Character Set"的缩写。
根据维基百科全书( http://zh.wikipedia.org/wiki/ )的记载:历史上存在两个试图独立 设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org) 。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。
在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的 工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了 与ISO 10646-1相同的字库和字码。
目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年 的Unicode 4.1.0。ISO的最新标准是ISO 10646-3:2003。
UCS只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是 6C49,我可以用4个ascii数字来传输、保存这个编码;也可以用utf-8编码:3个连续的字节 E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受 的方案。UTF-8的一个特别的好处是它与ISO- 8859-1完全兼容。UTF是“UCS Transformat ion Format”的缩写。
IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF -8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负 责维护的RFC是Internet上一切规范的基础。
2.1、内码和code page
目前Windows的内核已经采用Unicode编码,这样在内核上可以支持全世界所有的语言文字 。但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可 能不支持现有的编码,而全部改用Unicode。
Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到 的内码。GBK对应的code page是CP936。
微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而W indows的代码页只支持单字节和双字节编码,所以这个code page是无法真正使用的。
3、UCS-2、UCS-4、BMP
UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个 字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:
UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。
UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256 个plane。每个 plane根据第3个字节分为256行(rows),每行包含256个cells。当然同一行 的cells只是最后一个字节不同,其余都相同。
group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字 节为0的码位被称作BMP。
将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字 节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
4、UTF编码
UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
UCS-2编码(16进制) UTF-8 字节流(二进制) 0000 - 007F 0xxxxxxx 0080 - 07FF 110xxx xx 10xxxxxx 0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了: 1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001,用这个比特 流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
读者可以用记事本测试一下我们的编码是否正确。需要注意,UltraEdit在打开utf-8编码 的文本文件时会自动转换为UTF-16,可能产生混淆。你可以在设置中关掉这个选项。更好 的工具是Hex Workshop。
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码 对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使 用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基 本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节 序的问题。
5、UTF的字节序和BOM
UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个 UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E, “乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎” 还 是“乙”?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表 ,而是Byte Order Mark。BOM是一个有点小聪明的想法:
在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE 在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流 前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这 个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BR EAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所 以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
6、进一步的参考资料
本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode" ( http://ww w.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html )。
我还找了两篇看上去不错的资料,不过因为我开始的疑问都找到了答案,所以就没有看:
"Understanding Unicode A general introduction to the Unicode Standard" ( http: //scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter04a ) " Character set encoding basics Understanding character set encodings and legacy encodings" ( http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id =IWS-Chapter03 ) 我写过UTF-8、UCS-2、GBK相互转换的软件包,包括使用Windows API和 不使用Windows API的版本。以后有时间的话,我会整理一下放到我的个人主页上( http: //fmddlmyy.home4u.china.com )。
附录1 再说说区位码、GB2312、内码和代码页
有的朋友对文章中这句话还有疑问: “GB2312的原文还是区位码,从区位码到内码,需要 在高字节和低字节上分别加上A0。”
我再详细解释一下:
“GB2312的原文”是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字 编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数 称为“区”,第二个数称为“位”。所以也称为区位码。1-9区是中文符号,16-55 区是一 级汉字,56-87区是二级汉字。现在Windows也还有区位输入法,例如输入1601得到“啊” 。
内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的.现在的Windows 在内部统一使用Unicode,然后用代码页适应各种语言,“内码”的概念就比较模糊了。微 软一般将缺省代码页指定的编码说成是内码,在特殊的场合也会说自己的内码是Unicode, 例如在 GB18030问题的处理上。
所谓代码页(code page)就是针对一种语言文字的字符编码。例如GBK的code page是CP936 ,BIG5的code page是CP950,GB2312的code page是CP20936。
Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打 开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows应该去怎么解释它呢 ?
是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解 释?如果按GBK去解释,就会得到“汉字”两个字。按照其它编码解释,可能找不到对应的 字符,也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符,这时就产生了 乱码。
答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控 制面板的区域选项设置。记事本的另存为中有一项ANSI,其实就是按照缺省代码页的编码 方法保存。
Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使 用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可 以指定charset。
有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定ch arset。如果他使用了0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会 出现乱码。这时只要在这个html文件中加上指定charset的语句,例如:如果原作者使用的 代码页和ISO8859-1兼容,就不会出现乱码了。
再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII 编码冲突。为了兼容00-7f的 ASCII编码,我们在区位码的高、低字节上分别加上A0。这样 “啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然 GB2312的原 文根本没提到这一点。
在软件的编码和实现中,我们可能会碰到个 一个比较头疼的问题--编码,不同字符间的编码和解码,你确定了解各种字符的编码吗?一个朋友问到了我这 个问题,我虽然能回答一两个出来,但是感觉已经有点模糊,混乱了,在网上搜了搜,在书上翻了翻,总结一下吧。首先按照字符编码的历程来看:
1. ASCII
我们需要了解的最早编码是ASCII码。它用7个二进制位来表示,由于那个时期生产的大多数计算机使用8位大小的字节,因此用户不仅可以存放所有 可能的ASCII字符,而且有整整一位空余下来。如果你技艺高超,可以将该位用做自己离奇的目的:WordStar中那个发暗的灯泡实际上设置这个高位, 以指示一个单词中的最后一个字母,同时这也宣示了WordStar只能用于英语文本。
由于字节有多达8位的空间,因此许多人在想:“呀!我们 可以把128~255之间的编码用做个人的应用目的。”问题在于,同时产生这种想法的人相当多,而且在128~255之间的各个位置上应该存放什么这一问 题上,真是仁者见仁智者见智。事实上,只要人们开始在美国以外的地方购买计算机,那么各种各样的不同OEM字符集都会进入规划设计行列,并且各人都会根据 自己的需要使用高位的128个字符。如此一来,甚至在同语种的文档之间就不容易实现互换。 ASCII可被扩展,最优秀的扩展方案是ISO 8859-1,通常称之为Latin-1。Latin-1包括了足够的附加字符集来写基本的西欧语言。
最后,这个人人参与的OEM终于以ANSI标准的形式形成文件。在ANSI标准中,每个人都认同如何使用低端的128个编码,这与ASCII相当一致。不过,根据所在国籍的不同,处理编码128以上的字符有许多不同的方式。这些不同的系统称为代码页。
同时,甚至更为令人头疼的事情正在逐步上演,亚洲国家的字符表有成千上万个字符,这样的字符表是用8位二进制无法表示的。该问题的解决通常有赖于称为DBCS(double byte character set,双字节字符集)的繁杂字符系统。
不过,仍然需要指出一点,多数人还是姑且认为一个字节就是一个字符,以及一个字符就是8个二进制位,并且只要确保不将字符串从一台计算机移植到另一台计 算机,或者说一种以上的语言,那么这几乎总是可以凑合。当然,只要一进入Internet,从一台计算机向另一台计算机移植字符串就成为家常便饭了,而各 种复杂状况也随之呈现出来。令人欣慰的是,Unicode随即问世了。作用:表语英语及西欧语言。
位数:ASCII是用7位表示的,能表示128个字符;其扩展使用8位表示,表示256个字符。
范围:ASCII从00到7F,扩展从00到FF。2.iso8859-1
属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,字母'a'的编码为0x61=97。
很明显,iso8859-1编码表示的字符范围很窄,无法表示中文字符。但是,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍 旧使用iso8859-1编码来表示。而且在很多协议上,默认使用该编码。比如,虽然"中文"两个字不存在iso8859-1编码,以gb2312编码为 例,应该是"d6d0 cec4"两个字符,使用iso8859-1编码的时候则将它拆开为4个字节来表示:"d6 d0 ce c4"(事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是UTF编码,则是6个字节"e4 b8 ad e6 96 87"。很明显,这种表示方法还需要以另一种编码为基础。
作用:扩展ASCII,表示西欧、希腊语等。
位数:8位,
范围:从00到FF,兼容ASCII字符集。3. GB码字符集
全称是GB2312-80《信息交换用汉字编码字符集基本集》,1980年发布,是中文信息处理的国家标准,在大陆及海外使用简体中文的地区(如新加坡 等)是强制使用的唯一中文编码。P-Windows3.2和苹果OS就是以GB2312为基本汉字编码, Windows 95/98则以GBK为基本汉字编码、但兼容支持GB2312。
双字节编码
范围:A1A1~FEFE
A1-A9:符号区,包含682个符号
B0-F7:汉字区,包含6763个汉字4.GB2312字符集
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从 A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。GB2312-80中共收录了7545个字符,用两个字节编码一个 字符。每个字符最高位为0。GB2312-80编码简称国标码。
GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。
作用:国家简体中文字符集,兼容ASCII。
位数:使用2个字节表示,能表示7445个符号,包括6763个汉字,几乎覆盖所有高频率汉字。
范围:高字节从A1到F7, 低字节从A1到FE。将高字节和低字节分别加上0XA0即可得到编码。5. GB12345-90字符集
1990年制定了繁体字的编码标准GB12345-90《信息交换用汉字编码字符集第一辅助集》,目的在于规范必须使用繁体字的各种场合,以及古籍整理 等。该标准共收录6866个汉字(比GB2312多103个字,其它厂商的字库大多不包括这些字),纯繁体的字大概有2200余个。
双字节编码
范围:A1A1~FEFE
A1-A9:符号区,增加竖排符号
B0-F9:汉字区,包含6866个汉字6.GBK字符集
GBK编码(Chinese Internal Code Specification)是中国大陆制订的、等同于UCS的新的中文编码扩展国家标准。gbk编码能够用来同时表示繁体字和简体字,而gb2312只 能表示简体字,gbk是兼容gb2312编码的。GBK工作小组于1995年10月,同年12月完成GBK规范。该编码标准兼容GB2312,共收录汉字 21003个、符号883个,并提供1894个造字码位,简、繁体字融于一库。Windows95/98简体中文版的字库表层编码就采用的是GBK,通过 GBK与UCS之间一一对应的码表与底层字库联系。
英文名:Chinese Internal Code Specification
中文名:汉字内码扩展规范1.0版
双字节编码,GB2312-80的扩充,在码位上和GB2312-80兼容
范围:8140~FEFE(剔除xx7F)共23940个码位
包含21003个汉字,包含了ISO/IEC 10646-1中的全部中日韩汉字作用:它是GB2312的扩展,加入对繁体字的支持,兼容GB2312。
位数:使用2个字节表示,可表示21886个字符。
范围:高字节从81到FE,低字节从40到FE。7. BIG5字符集
是目前台湾、香港地区普遍使用的一种繁体汉字的编码标准,包括440个符号,一级汉字5401个、二级汉字7652个,共计13060个汉字。BIG5又 称大五码或五大码,1984年由台湾财团法人信息工业策进会和五间软件公司宏碁 (Acer)、神通 (MiTAC)、佳佳、零壹 (Zero One)、大众 (FIC)创立,故称大五码。Big5码的产生,是因为当时台湾不同厂商各自推出不同的编码,如倚天码、IBM PS55、王安码等,彼此不能兼容;另一方面,台湾政府当时尚未推出官方的汉字编码,而中国大陆的GB2312编码亦未有收录繁体中文字。
Big5字符集共收录13,053个中文字,该字符集在中国台湾使用。耐人寻味的是该字符集重复地收录了两个相同的字:“兀”(0xA461及0xC94A)、“嗀”(0xDCD1及0xDDFC)。
Big5码使用了双字节储存方法,以两个字节来编码一个字。第一个字节称为“高位字节”,第二个字节称为“低位字节”。高位字节的编码范围0xA1-0xF9,低位字节的编码范围0x40-0x7E及0xA1-0xFE。
尽管Big5码内包含一万多个字符,但是没有考虑社会上流通的人名、地名用字、方言用字、化学及生物科等用字,没有包含日文平假名及片假字母。
例如台湾视“着”为“著”的异体字,故没有收录“着”字。康熙字典中的一些部首用字(如“亠”、“疒”、“辵”、“癶”等)、常见的人名用字(如“堃”、“煊”、“栢”、“喆”等) 也没有收录到Big5之中
8.GB18030字符集
GB 18030-2000全称是《信息技术信息交换用汉字编码字符集基本集的扩充》,由信息产业部和原国家质量技术监督局于2000年3月17日联合发布,作为国家强制性标准自发布之日起实施。
为了适应信息处理技术快速发展的需要,1998年10月,由信息产业部电子四所、北京大学计算机技术研究所、北大方正集团、新天地公司、四通新世纪公司、 中科院软件所、长城软件公司、中软总公司、金山软件公司和联想公司的技术人员组成标准起草组。在标准研制过程中,全国信息技术标准化技术委员会多次召集标 准起草组和知名公司对标准草案进行充分地研究论证,并且特邀了微软公司、惠普公司、Sun公司和IBM公司等参加,广泛征求意见。标准起草组经过反复斟酌 和验证,提出了标准制定原则——与GB 2312信息处理交换码所对应的事实上的内码标准兼容,在字汇上支持GB 13000.1的全部中、日、韩(CJK)统一汉字字符和全部CJK扩充A的字符,并且确定了编码体系和27484个汉字,形成兼容性、扩展性、前瞻性兼 备的方案。
该标准采用单字节、双字节和四字节三种方式对字符编码。作用:它解决了中文、日文、朝鲜语等的编码,兼容GBK。
位数:它采用变字节表示(1 ASCII,2,4字节)。可表示27484个文字。
范围:1字节从00到7F; 2字节高字节从81到FE,低字节从40到7E和80到FE;4字节第一三字节从81到FE,第二四字节从30到39。9.通用字符集(UCS)字符集
ISO/IEC 10646-1 [ISO-10646]定义了一种多于8比特字节的字符集,称作通用字符集(UCS),它包含了世界上大多数可书写的字符系统。已定义了两种多8比特字节 编码,对每一个字符采用四个8比特字节编码的称为UCS-4,对每一个字符采用两个8比特字节编码的称为UCS-2。它们仅能够对UCS的前64K字符进 行编址,超出此范围的其它部分当前还没有分配编址。
作用:国际标准 ISO 10646 定义了通用字符集 (Universal Character Set)。它是与UNICODE同类的组织,UCS-2和UNICODE兼容。
位数:它有UCS-2和UCS-4两种格式,分别是2字节和4字节。
范围:目前,UCS-4只是在UCS-2前面加了0x0000。10.Unicode字符集
Unicode字符集(简称为UCS),国际标准组织于1984年4月成立ISO/IEC JTC1/SC2/WG2工作组,针对各国文字、符号进行统一性编码。1991年美国跨国公司成立Unicode Consortium,并于1991年10月与WG2达成协议,采用同一编码字集。目前Unicode是采用16位编码体系,其字符集内容与 ISO10646的BMP(Basic Multilingual Plane)相同。Unicode于1992年6月通过DIS(Draf International Standard),目前版本V2.0于1996公布,内容包含符号6811个,汉字20902个,韩文拼音11172个,造字区6400个,保留 20249个,共计65534个。Unicode编码后的大小是一样的.例如一个英文字母 "a" 和 一个汉字 "好",编码后都是占用的空间大小是一样的,都是两个字节!
Unicode可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。所以可以说它是不兼容iso8859-1编码的, 也不兼容任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母'a'为"00 61"。
需要说明的是,定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用unicode编码来处理的,比如java。
UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16,UTF-32和UTF-7编码。
UTF-8
UTF:UCS Transformation Format.考虑到unicode编码不兼容iso8859-1编码,而且容易占用更多的空间:因为对于英文字母,unicode也需要两个字节来表 示。所以unicode不便于传输和存储。因此而产生了utf编码,utf编码兼容iso8859-1编码,同时也可以用来表示所有语言的字符,不 过,utf编码是不定长编码,每一个字符的长度从1-6个字节不等。另外,utf编码自带简单的校验功能。一般来讲,英文字母都是用一个字节表示,而汉字 使用三个字节。
注意,虽然说utf是为了使用更少的空间而使用的,但那只是相对于unicode编码来说,如果已经知道是汉字,则使用GB2312/GBK无疑是 最节省的。不过另一方面,值得说明的是,虽然utf编码对汉字使用3个字节,但即使对于汉字网页,utf编码也会比unicode编码节省,因为网页中包 含了很多的英文字符。
UTF8编码后的大小是不一定,例如一个英文字母"a" 和 一个汉字 "好",编码后占用的空间大小就不样了,前者是一个字节,后者是三个字节!编码的方法是从低位到高位。黄色为标志位其它着色为了显示其,编码后的位置。
UTF-16
采用2 字节,Unicode中不同部分的字符都同样基于现有的标准。这是为了便于转换。从 0x0000到0x007F是ASCII字符,从0x0080到0x00FF是ISO-8859-1对ASCII的扩展。希腊字母表使用从0x0370到 0x03FF 的代码,斯拉夫语使用从0x0400到0x04FF的代码,美国使用从0x0530到0x058F的代码,希伯来语使用从0x0590到0x05FF的代 码。中国、日本和韩国的象形文字(总称为CJK)占用了从0x3000到0x9FFF的代码;
由于0x00在c语言及操作系统文件名等中有特殊意义,故很多情况下需要UTF-8编码保存文本,去掉这个0x00。举例如下:
UTF-16: 0x0080 = 0000 0000 1000 0000
UTF-8: 0xC280 = 1100 0010 1000 0000
UTF-32采用4字节。
UTF-7
A Mail-Safe Transformation Format of Unicode(RFC1642)。这是一种使用 7 位 ASCII 码对 Unicode 码进行转换的编码。它的设计目的仍然是为了在只能传递 7 为编码的邮件网关中传递信息。 UTF-7 对英语字母、数字和常见符号直接显示,而对其他符号用修正的 Base64 编码。符号 + 和 - 号控制编码过程的开始和暂停。所以乱码中如果夹有英文单词,并且相伴有 + 号和 - 号,这就有可能是 UTF-7 编码。
作用:为世界650种语言进行统一编码,兼容ISO-8859-1。
位数:UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16和UTF-32。很多人以为UTF-8等和Unicode都是字符集或都是编码方式,其实这是误区。
到以上为止,大部分常用的字符集已经基本列举完毕,再看一些其他的编码方式:
MIME 编码
MIME 是“多用途网际邮件扩充协议”的缩写,在 MIME 协议之前,邮件的编码曾经有过 UUENCODE 等编码方式 ,但是由于 MIME 协议算法简单,并且易于扩展,现在已经成为邮件编码方式的主流,不仅是用来传输 8 bit 的字符,也可以用来传送二进制的文件 ,如邮件附件中的图像、音频等信息,而且扩展了很多基于MIME 的应用。从编码方式来说,MIME 定义了两种编码方法Base64与QP(Quote-Printable)Base64
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。
为什么要使用Base64?
在设计这个编码的时候,我想设计人员最主要考虑了3个问题:
1.是否加密?
2.加密算法复杂程度和效率
3.如何处理传输?
加密是肯定的,但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是“防君子不防小人”。即达到一眼望去完全看不出内容即可。
基 于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似,MIME协议等用于发送Email的协议解决的是如何收发Email,而并不 是如何安全的收发Email。因此算法的复杂程度要小,效率要高,否则因为发送Email而大量占用资源,路就有点走歪了。
但是,如果是基于以上两点,那么我们使用最简单的恺撒法即可,为什么Base64看起来要比恺撒法复杂呢?这是因为在Email的传送过程中,由于历史原 因,Email只被允许传送ASCII字符,即一个8位字节的低7位。因此,如果您发送了一封带有非ASCII字符(即字节的最高位是1)的Email通 过有“历史问题”的网关时就可能会出现问题。网关可能会把最高位置为0!很明显,问题就这样产生了!因此,为了能够正常的传送Email,这个问题就必须 考虑!所以,单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。
基于以上的一些主要原因产生了Base64编码。Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。
QP(Quote-Printable)
通常缩写为“Q”方法,其原理是把一个 8 bit 的字符用两个16进制数值表示,然后在前面加“=”。所以我们看到经过QP编码后的文件通常是这个样子:=B3=C2=BF=A1=C7=E5=A3=AC=C4=FA=BA=C3=A3=A1。
最后,我们希望你看了这篇文章之后不要混淆字符集和字符编码的概念,还有对以上谈到的各种编码方式的原因有大致的了解,象utf-8这类是为了解析 unicode这种字符集而制定,而base64这类是为了解决实际的网络应用而制定。为了让你便于记忆,对先前介绍的字符集进行统计和分类:
语言 字符集 正式名称 英语、西欧语 ASCII,ISO-8859-1 简体中文 GB2312 MBCS 多字节 繁体中文 BIG5 MBCS 多字节 简繁中文 GBK MBCS 多字节 中文、日文及朝鲜语 GB18030 MBCS 多字节 各国语言 UNICODE,UCS DBCS 宽字节
关于”unicode字符是2个字节“这句话的讨论
unicode是一个标准,也可以说是世界上的语言字符和数字映射的一种标准。它没有限制字符的数量,但是可能这个标准规定的映射只是映射了一部分字符。每个字符映射一个code point(码点)。Unicode 目前规划的总空间是17个平面(平面0至16),0x0000 至 0x10FFFF。每个平面有 65536 个码点。
所以Unicode支持的字符上限是65536个 这句话也是有问题的,这65536个字符是我们最常用的基本字符,但是还有很多字符是在0x0000--0xFFFF之外的。在我们说unicode是几个字节这句话的时候,就已经暗示了我们现在讨论的是unicode在计算机内存里的存储方式了(只有在计算机系统里才有字节这种定义,如果脱离计算机的话unicode是不会与字节扯上关系的)。unicode存储在计算机内存里肯定是需要编码的,那么就有UTF-8,UTF-16,UTF-32等编码方案。为什么我们常说“unicode字符是2个字节”,是因为windows默认的unicode编码就是UTF-16。而unicode在这种编码下,大部分都是2个字节的,至少上面提到的前65536个基本常用字符都是2个字节的。所以就有了“unicode字符是2个字节”这句话。其实UTF-16是一种变长的编码方案,有些unicode字符的抽象码点(code point)编码后是2个字节,有些是4个字节。所以“unicode字符是2个字节”这句话大多数时候是正确的,但是unicode字符的抽象码点(code point)超过范围0x0000--0xFFFF后就不对了。
如果是为了跨平台兼容性,只需要知道,在 Windows 记事本的语境中:
- 所谓的「ANSI」指的是对应当前系统 locale 的遗留(legacy)编码。
- 所谓的「Unicode」指的是带有 BOM 的小端序 UTF-16。
- 所谓的「UTF-8」指的是带 BOM 的 UTF-8。
下面是关于上面windows语境的一些词语的解释:
1.BOM(byte order mark)是为 UTF-16 和 UTF-32 准备的,用于标记字节序(byte order)。微软在 UTF-8 中使用 BOM 是因为这样可以把 UTF-8 和 ASCII 等编码明确区分开,但这样的文件在 Windows 之外的操作系统里会带来问题。
2.「UTF-8」和「带 BOM 的 UTF-8」的区别就是有没有 BOM。即文件开头有没有 U+FEFF。所以在谈及unicode的编码方案时是离不开计算机的微处理器的(决定字节顺序,是大端还是小端),以及操作系统(比如windows默认的上述语境:unicode指UTF-16).注意:编码的目的在于在计算机系统里利于存储(减少空间占用),在网络传输里利于传输(减少数据量,不用考虑字节顺序的问题等)。其实就是把抽象的东西,编码成10等二进制字节流,并且尽量的减少内存空间的占用和网络传输时带宽的占用。
posted on 2017-04-04 14:05 everest33 阅读(1092) 评论(0) 编辑 收藏 举报