近日需要不一样的编码,关于上述编码,一直迷迷糊糊,查了些资料,总算大体了解了,
如下全是从网上搜来的:算法
1. ASCII和Ansi编码
字符内码(charcter code)指的是用来表明字符的内码.读者在输入和存储文档时都要使用内码,内码分为
单字节内码 -- Single-Byte character sets (SBCS),可以支持256个字符编码.
双字节内码 -- Double-Byte character sets)(DBCS),可以支持65000个字符编码.
前者即为ASCII编码,后者相应ANSI.
至于中文简体编码GB2312,实际上它是ANSI的一个代码页936
2. Unicode
如上,ANSI有很是多代码页,使用不一样代码页的内码没法在其它代码也正常显示,这就是为何日文版/繁体中文版游戏没法在中文简体平台直接显示的缘由.
Unicode也是一种字符编码方法,只是它是由国际组织设计,可以容纳全世界所有语言文字的编码方案.它是一种2字节编码,可以提供65536个字符,这个数字是不够表示所有的字符的(汉语就有55000多字符),因此,经过一个代理对的机制来实现附加的917,476个字符表示,以达到所有字符都具备惟一编码.
3.Unicode和BigEndianUnicode
这二者仅仅是存储顺序不一样,如"A"的unicode编码为65 00
其BigEndianUnicode编码为00 65
4. UTF-8
这是为传输而设计的编码,其系列还有UTF-7和UTF-16
当中UTF-16和Unicode编码大体同样, UTF-8就是以8位为单元对Unicode进行编码。从Unicode到UTF-8的编码方式例如如下:
Unicode编码(16进制) UTF-8 字节流(二进制)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 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。
windows
ANSI:系统预设的标准文字储存格式。ANSI是American National Standards Institute的缩写。它成立于1918年,是一个自愿性的组织,拥有超过1300个会员,包含所有大型的电脑公司。ANSI专为电脑工业创建标准,它是世界上至关重要的标准。
Unicode:世界上所有主要指令文件的联集,包含商业和我的电脑所使用的公用字集。当採用Unicode格式储存文件时,可以使用Unicode控制字符辅助说明语言的文字覆盖范围,如阿拉伯语、希伯来语。用户在“记事本”中输入含有Unicode字符的文字并储存文件时,系统会提示你必须选取“另存为”中的Unicode编码,这些字符才不会被遗失。需要提醒你们的是,部分Windows 2000字型没法显示所有的Unicode字符。假设发现文件里缺乏了某些字符,仅仅需将其变动为其余字型就能够。
Unicode big endian:在Big-endian处理器(如苹果Macintosh电脑)上创建的Unicode文件里的文字位元组(存放单位)排列顺序,与在Intel处理器上创建的文件的文字位元组排列顺序相反。最重要的位元组拥有最低的地址,且会先储存文字中较大的一端。为使这类电脑的用户能够存取你的文件,可选择Unicode big-endian格式。
UTF-8:UTF意为通用字集转换格式(Universal Character Set Transformation Format),UTF-8是Unicode的8位元格式。假设使用仅仅能在同类位元组内支持8个位元的重要资料一类的旧式传输媒体,可选择UTF-8格式。
安全
Unicode是一种字符编码规范 。
先从ASCII提及。ASCII是用来表示英文字符的一种编码规范,每个ASCII字符占用1个字节(8bits)
所以,ASCII编码可以表示的最大字符数是256,事实上英文字符并无那么多,通常仅仅用前128个(最高位为0),当中包含了控制字符、数字、大写和小写字母和其它一些符号
。
而最高位为1的另128个字符被成为“扩展ASCII”,通常用来存放英文的制表符、部分音标字符等等的一些其它符号
这样的字符编码规范显然用来处理英文没有什么问题
。(实际上也可以用来处理法文、德文等一些其它的西欧字符,但是不能和英文通用),但是面对中文、阿拉伯文之类复杂的文字,255个字符显然不够用
因而,各个国家纷纷制定了本身的文字编码规范,当中中文的文字编码规范叫作“GB2312-80”,它是和ASCII兼容的一种编码规范,事实上就是利用扩展ASCII没有真正标准化这一点,把一个中文字符用两个扩展ASCII字符来表示。
但是这种方法有问题,最大的问题就是,中文文字没有真正属于本身的编码,因为扩展ASCII码尽管没有真正的标准化,但是PC里的ASCII码仍是有一个事实标准的(存放着英文制表符),因此很是多软件利用这些符号来画表格。这样的软件用到中文系统中,这些表格符就会被误认做中文字,破坏版面。而且,统计中英文混合字符串中的字数,也是比較复杂的,咱们必须推断一个ASCII码是否扩展,以及它的下一个ASCII是否扩展,而后才“猜”那多是一个中文字
。
总之当时处理中文是很是痛苦的。而更痛苦的是GB2312是国家标准,台湾当时有一个Big5编码标准,很是多编码和GB是一样的,因此……,嘿嘿。
这时候,咱们就知道,要真正解决中文问题,不能从扩展ASCII的角度入手,也不能仅靠中国一家来解决。而必须有一个全新的编码系统,这个系统要可以将中文、英文、法文、德文……等等所有的文字统一块儿来考虑,为每个文字都分配一个单独的编码,这样才不会有上面那种现象出现。
因而,Unicode诞生了。
Unicode有两套标准,一套叫UCS-2(Unicode-16),用2个字节为字符编码,还有一套叫UCS-4(Unicode-32),用4个字节为字符编码。
以眼下常用的UCS-2为例,它可以表示的字符数为2^16=65535,基本上可以容纳所有的欧美字符和绝大部分的亚洲字符
。
UTF-8的问题后面会提到 。
在Unicode里,所有的字符被一视同仁。汉字再也不使用“两个扩展ASCII”,而是使用“1个Unicode”,注意,现在的汉字是“一个字符”了,因而,拆字、统计字数这些问题也就天然而然的攻克了
。
但是,这个世界不是理想的,不可能在一晚上之间所有的系统都使用Unicode来处理字符,因此Unicode在诞生之日,就必须考虑一个严峻的问题:和ASCII字符集之间的不兼容问题。
咱们知道,ASCII字符是单个字节的,比方“A”的ASCII是65。而Unicode是双字节的,比方“A”的Unicode是0065,这就形成了一个很是大的问题:之前处理ASCII的那套机制不能被用来处理Unicode了
。
还有一个更加严重的问题是,C语言使用'/0'做为字符串结尾,而Unicode里偏偏有很是多字符都有一个字节为0,这样一来,C语言的字符串函数将没法正常处理Unicode,除非把世界上所有用C写的程序以及他们所用的函数库所有换掉
。
因而,比Unicode更伟大的东东诞生了,之因此说它更伟大是因为它让Unicode再也不存在于纸上,而是真实的存在于咱们你们的电脑中。那就是:UTF
。
UTF= UCS Transformation Format UCS转换格式
它是将Unicode编码规则和计算机的实际编码相应起来的一个规则。现在流行的UTF有2种:UTF-8和UTF-16
。
当中UTF-16和上面提到的Unicode自己的编码规范是一致的,这里很少说了。而UTF-8不一样,它定义了一种“区间规则”,这样的规则可以和ASCII编码保持最大程度的兼容
。
UTF-8有点类似于Haffman编码,它将Unicode编码为00000000-0000007F的字符,用单个字节来表示;
00000080-000007FF的字符用两个字节表示
00000800-0000FFFF的字符用3字节表示
因为眼下为止Unicode-16规范没有指定FFFF以上的字符,因此UTF-8最可能是使用3个字节来表示一个字符。但理论上来讲,UTF-8最多需要用6字节表示一个字符。
在UTF-8里,英文字符仍然跟ASCII编码同样,所以原先的函数库可以继续使用。而中文的编码范围是在0080-07FF之间,所以是2个字节表示(但这两个字节和GB编码的两个字节是不一样的),用专门的Unicode处理类可以对UTF编码进行处理。
如下说说中文的问题。
因为历史的缘由,在Unicode以前,一共存在过3套中文编码标准。
GB2312-80,是中国大陆使用的国家标准,当中一共编码了6763个常用简体汉字。Big5,是台湾使用的编码标准,编码了台湾使用的繁体汉字,大概有8千多个。HKSCS,是中国香港使用的编码标准,字体也是繁体,但跟Big5有所不一样。
这3套编码标准都採用了两个扩展ASCII的方法,所以,几套编码互不兼容,而且编码区间也各有不一样
因为其不兼容性,在同一个系统中同一时候显示GB和Big5基本上是不可能的。当时的南极星、RichWin等等软件,在本身主动识别中文编码、本身主动显示正确编码方面都作了很是多努力
。
他们用了如何的技术我就不得而知了,我知道好像南极星之前以同屏显示繁简中文为卖点。
后来,因为各方面的缘由,国际上又制定了针对中文的统一字符集GBK和GB18030,当中GBK已经在Windows、Linux等多种操做系统中被实现。
GBK兼容GB2312,并添加了大量不常用汉字,还添加了差点儿所有的Big5中的繁体汉字。但是GBK中的繁体汉字和Big5中的差点儿不兼容。
GB18030至关因而GBK的超集,比GBK包含的字符不少其它。据我所知眼下尚未操做系统直接支持GB18030。
谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词
这是一篇程序猿写给程序猿的趣味读物。所谓趣味是指可以比較轻松地了解一些原来不清楚的概念,增进知识,类似于打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-3二、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称做“大尾”和“小尾”。
一、字符编码、内码,顺带介绍汉字编码
字符必须编码后才干被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用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、GB23十二、GBK到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中老是有一样的编码,后面的标准支持不少其它的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。依照程序猿的称呼,GB23十二、GBK到GB18030都属于双字节字符集
(DBCS)。
有的中文Windows的缺省内码仍是GBK,可以经过GB18030升级包升级到GB18030。只是GB18030相对GBK添加的字符,普通人是很是难用到的,一般咱们仍是用GBK指代中文Windows内码。
这里还有一些细节:
GB2312的原文仍是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
在DBCS中,GB内码的存储格式始终是big endian,即高位在前。
GB2312的两个字节的最高位都是1。但符合这个条件的码位仅仅有128*128=16384个。因此GBK和GB18030的低字节最高位均可能不是1。只是这不影响DBCS字符流的解析:在读取DBCS字符流时,仅仅要遇到高位为1的字节,就可以将下两个字节做为一个双字节编码,而不用管低字节的高位是什么。
二、Unicode、UCS和UTF
前面提到从ASCII、GB23十二、GBK到GB18030的编码方法是向下兼容的。而Unicode仅仅与ASCII兼容(更准确地说,是与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的最新标准是10646-3:2003。
UCS规定了怎么用多个字节表示各类文字。如何传输这些编码,是由UTF(UCS
Transformation Format)规范规定的,常见的UTF规范包含UTF-八、UTF-七、UTF-16。
IETF的RFC2781和RFC3629以RFC的一向风格,清楚、明快又不失严谨地描写叙述了UTF-16和UTF-8的编码方法。我老是记不得IETF是Internet
Engineering Task
Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。
三、UCS-二、UCS-四、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以外。
四、UTF编码
UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式例如如下:
UCS-2编码(16进制) UTF-8 字节流(二进制)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 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。
读者可以用记事本測试一下咱们的编码是否正确。
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码相应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。只是因为实际使用的UCS2,或者UCS4的BMP一定小于0x10000,因此就眼下而言,可以以为UTF-16和UCS-2基本一样。但UCS-2仅仅是一个编码方案,UTF-16却要用于实际的传输,因此就不得不考虑字节序的问题。
五、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-BREAK SPACE"的UTF-8编码是EF BB
BF(读者可以用咱们前面介绍的编码方法验证一下)。因此假设接收者收到以EF BB
BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
函数
系统支持
Windows 98 :仅仅支持ANSI。
Windows 2k :既支持ANSI又支持UNICODE。
Windows CE :仅仅支持UNICODE。
Windows 2000整个OS系统都是基于UNICODE的,为此在windows 2000下使用ANSI是需要付出代价的,尽管在编码上不用不论什么的转换,但是这样的转化是隐藏的,是占用系统资源的(CPU,内存)。
在Windows 98下必须使用UNICODE,则需要本身手动的编码切换。
字体
在计算机中字符一般并不是保存为图像,每个字符都是使用一个编码来表示的,而每个字符到底使用哪一个编码表明,要取决于使用哪一个字符集(charset)。
在最初的时候,Internet上仅仅有一种字符集——ANSI的ASCII字符集,它使用7 bits来表示一个字符,总共表示128个字符,当中包含了英文字母、数字、标点符号等常用字符。以后,又进行扩展,使用8 bits表示一个字符,可以表示256个字符,主要在原来的7 bits字符集的基础上增长了一些特殊符号好比制表符。
后来,由于各国语言的增长,ASCII已经不能知足信息交流的需要,所以,为了可以表示其余国家的文字,各国在ASCII的基础上制定了本身的字符集,这些从ANSI标准派生的字符集被习惯的统称为ANSI字符集,它们正式的名称应该是MBCS(Multi-Byte Chactacter System,即多字节字符系统)。这些派生字符集的特色是以ASCII 127 bits为基础,兼容ASCII 127,他们使用大于128的编码做为一个Leading Byte,紧跟在Leading Byte后的第二(甚至第三)个字符与Leading Byte一块儿做为实际的编码。这样的字符集有很是多,咱们常见的GB-2312就是当中之中的一个。
好比在GB-2312字符集中,“连通”的编码为C1 AC CD A8,当中C1和CD就是Leading Byte。前127个编码为标准ASCII保留,好比“0”的编码是30H(30H表示十六进制的30)。软件在读取时,假设看到30H,知道它小于128就是标准ASCII,表示“0”,看到C1大于128就知道它后面有一个另外的编码,所以C1 AC一同构成一个整个的编码,在GB-2312字符集中表示“连”。
由于每种语言都制定了本身的字符集,致使最后存在的各类字符集实在太多,在国际交流中要常常转换字符集很是不便。所以,提出了Unicode字符集,它固定使用16 bits(两个字节、一个字)来表示一个字符,共可以表示65536个字符。将世界上差点儿所有语言的常用字符收录当中,方便了信息交流。标准的Unicode称为UTF-16。后来为了双字节的Unicode可以在现存的处理单字节的系统上正确传输,出现了UTF-8,使用类似MBCS的方式对Unicode进行编码。注意UTF-8是编码,它属于Unicode字符集。Unicode字符集有多种编码形式,而ASCII仅仅有一种,大多数MBCS(包含GB-2312)也仅仅有一种。
好比“连通”两个字的Unicode标准编码UTF-16 (big endian)为:DE 8F 1A 90
而其UTF-8编码为:E8 BF 9E E9 80 9A
最后,当一个软件打开一个文本时,它要作的第一件事是决定这个文本究竟是使用哪一种字符集的哪一种编码保存的。软件有三种途径来决定文本的字符集和编码:
最标准的途径是检測文本最开头的几个字节,例如如下表:
开头字节 Charset/encoding
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.
好比插入标记后,连通”两个字的UTF-16 (big endian)和UTF-8码分别为:
FF FE DE 8F 1A 90
EF BB BF E8 BF 9E E9 80 9A
但是MBCS文本没有这些位于开头的字符集标记,更不幸的是,一些早期的和一些设计不良的软件在保存Unicode文本时不插入这些位于开头的字符集标记。所以,软件不能依赖于这样的途径。这时,软件可以採取一种比較安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户,好比将那个“连通”文件拖到MS Word中,Word就会弹出一个对话框。
假设软件不想麻烦用户,或者它不方便向用户请示,那它仅仅能採取本身“猜”的方法,软件可以依据整个文本的特征来推測它可能属于哪一个charset,这就很是可能不许了。使用记事本打开那个“连通”文件就属于这样的状况。
咱们可以证实这一点:在记事本中键入“连通”后,选择“Save As”,会看到最后一个下拉框中显示有“ANSI”,这时保存。当再当打开“连通”文件出现乱码后,再点击“File”->“Save As”,会看到最后一个下拉框中显示有“UTF-8”,这说明记事本以为当前打开的这个文本是一个UTF-8编码的文本。而咱们刚才保存时是用ANSI字符集保存的。这说明,记事本推測了“连通”文件的字符集,以为它更像一个UTF-8编码文本。这是由于“连通”两个字的GB-2312编码看起来更像UTF-8编码致使的,这是一个巧合,不是所有文字都这样。可以使用记事本的打开功能,在打开“连通”文件时在最后一个下拉框中选择ANSI,就能正常显示了。反过来,假设以前保存时保存为UTF-8编码,则直接打开也不会出现故障。
假设将“连通”文件放入MS Word中,Word也会以为它是一个UTF-8编码的文件,但它不能肯定,所以会弹出一个对话框询问用户,这时选择“中文简体(GB2312)”,就能正常打开了。记事本在这一点上作得比較简化罢了,这与这个程序的定位是一致的。
编码