文章主要是要讲解我们经常会在浏览器(xml,html)中遇到的一些编码,以及unicode,gbk,utf-8中的一些简要关系,以及后端的一些关联。
OK,让我们先看下场景1 从一个URL中看起: http://www.baidu.com/s?wd=%E6%88%91%E6%98%AF%E5%A5%BD%E4%BA%BA 肯定有人奇怪wd=后面一串是什么玩意? 你可以用JavaScript中的decodeUri解析出来: decodeURI("%E6%88%91%E6%98%AF%E5%A5%BD%E4%BA%BA") //输出 "我是好人" 这里,推荐你阅读下,这篇文章。在这篇文章中,你会知道,为什么用decodeURI可以解码出,为什么unescape不可以。 言归正传,这串奇怪的字符到底是怎么出来的呢? 其实就是将UTF-8版的“我是好人”,传入了encodeURI,然后得到的。 这里有两点要注意, 1、本身文字内容的编码是UTF-8 2、传入encodeURI,其实就等于又进行了一次urlencode 没有错,做了两种编码操作,这就是为什么这个代码看上去这么怪的原因。 ok,这里先说这个情况。接下来,让我们看场景2: escape("我是好人") //输出 "%u6211%u662F%u597D%u4EBA" 这一串又是什么?这个是unicode,其实真正的unicode码是6211,只是为了表示是unicode,js里面都会加上%u。 再看个场景3 将下面这段复制到Html文本中,然后用浏览器显示:
--------------------
对于标题中所说的不同编码方式,我们下面逐一剖析。
1、URL ENCODING
URLs只可以使用ASCII字符集在Internet上传输。
由于URLs经常会含有ASCII集之外的字符,此时URL就不得不将这些非ASCII的字符转换成有效的ASCII字符。
另外,URL encoding还会将不安全的ASCII字符使用以“%”开头后面紧跟两个十六进制数的形式,进行替换。
注意:
URLs是不能包含空格的。URL encoding一般会使用“+”号去替换空格。当然,也可以使用%20,这两种其实是一样的,只是一般前者是使用如下代码会得到
<form name="input" target="_blank" action="html_form_submit.asp" method="get">
<input type="text" value="Hello Günter" name="text" size="30">
<input type="submit" value="Submit">
</form>
当在input中填入一个空格,然后点击Submint时,会自动跳转到URL:“/html_form_submit.asp?text=+”,注意此处的空格变成了+
当使用JavaScript的escape()函数进行处理的时候,便会将空格处理成%20
重要解释:
其实URL encoding对于整个ascii都进行了编码,比如A为%41,B为%42,但是并一定会用到。因为URL本身就可以传输ascii,在前面也说了,有必要替换的无非也就是不安全的一些ascii数值,比如[、<之类的。那既然如此为什么还要对A等等进行编码呢?其实,这里应该抽象出去看。说到底,ASCII也就是一些二进制数(或说是十六进制数),他现有这些数值,然后让A,B去对应了这些值而已。这样的话,当对于例如UTF的情况,中文为三个字节,就可以认为是3个ASCII组成,比如一个中文字我,对应的ASCII码,如果拆成单个字节的ASCII码看的话,正好分别是 æ^',但是这是没有意义的,所以对于不同的编码会如何确认多少个字节为一组,因此我们传过去最好的传的是特定的编码(此处我的URL encoding为%E6%88%91,%后的其实就是对应的编码,可以认为组成了一组编号。)当后端接收到该字符后,再用特定的函数,可以对这些进行过URL encoding处理的字符串,再所反向处理。这也就是为什么这些反向处理函数需要选择当前传入的这些字符串是UTF8还是GBK,不同的语言决定了如何分段这些编码。这些处理程序,会把被URL encoding处理过的字符,根据指定的编码格式,然后解码出来。显示成我们人能读懂的格式。
所以这里看到的E6%88%91,你也可以认为是机器码,不同的机器码对应了不同的自然界符号。当然这些定义是我们人规定的。
1、ASCII
ASCII字符集被用于通过Internet在计算机间传递信息。ASCII是American Standard Code for Information Interchange的缩写。设计于60年代,并作为一种标准字符集广泛应用于计算机和其他硬件设备中。
ASCII是7位的字符集含有128字符,注意后来因为发展的需要,不够表示了,符号多了,于是将ASCII扩展成了8位的,正好一个字节,可以代表256个字符。扩展的主要是些全球其他国家比较通用的符号,比如£这个英镑符号,¥人民币符号,这些在最原始的ASCII码中是不包括的。当然这个改变后的ASCII不再叫ASCII,而是ISO 8859-1,又因为里面新增的字符时拉丁语,所以又成Latin-1.这就是著名的Latin编码方式。
这里主要说下原始的ASCII,其中主要包括了0-9的数字,a-z的字母(包括大写和小写)和一些特殊字符。
ASCII字符集广泛应用于现代计算机中,HTML,Internet都是基于ASCII的。
注意,这里有个很重要的理解,就是什么才是编码,是二进制或十六进制数才是编码,其对应的符号,只是图像。编码配合解码函数-》可读的符号。所以,要清楚编码是进制数。
这里着重讲下HTML Entity Code。在这个概念中有两个概念,一个事Entity Name,比如 ©s;等等,还有个是Entity Number,比如 ,©和前面的Entity Name是对应关系。为什么要引入HTML Entity Code呢?你想想,我们现在键盘能够表示键入的符号有多少个呢?你能在键盘上找到版权的符号吗?你是找不到的,但是明明ASCII中有,那么我们怎么才能输入呢。。。那么就是转码,就是用键盘可以输入的字符,组成一定的组合变成,键盘无法输入但是我们需要且ASCII中有的符号。OK。就这样。HTML Entity Code诞生了。那么Name和Number什么区别呢?就是两种表示方式而已。之所以没有用ASCII码直接显示,而是要再使用ASCII中转下的再显示的原因。主要还是我们的键盘。。表现力有限。如果可以把整个ASCII都能从键盘输入。那么也就没有必要这么中转了。
1、ANSI
这个编码的出现,主要是因为需求的不断扩展,需要表示的符号,不断增多。但是其代表的是一种编码方式。即使用2个字节代表一个字符的延伸编码方式。这个方式没有没有严格的标准或规则去统揽全局。。。于是乎,编码的世界进入了一篇混沌。比方说,在简体中文系统中,ANSI编码代表GB2312编码,日文系统中ANSI编码代表JIS编码。不同的ANSI编码是不兼容的,想想也知道。。。肯定是由冲突的。相同的编码对应着不同的字符。这下就麻烦了。。。。不同的编码,如果使用两种不同的不同的编码方式,肯定显示不一样。。。,这就出现个问题,中文系统的东西。。日文系统看过去就是一堆乱码。。我如果想再日文系统中能显示中文,该怎么做呢?除非同时安装有日文语言包和中文语言包,否则很遗憾。。。没有办法。。。于是乎,为了解决这个问题,我需要在日文系统中能显示中文,那么我就需要一个统一编码方式,里面不仅含有中文的编码,还有日文的编码。。。所以,扩大编码的格式是必须的了。同时还有个更麻烦的问题,在同一套系统中如果处理多套的编码格式。。。OMG,你的处理程序估计就很混乱了。因此出现了一统江湖的Unicode。
当然ANSI并不是一无是处,对于强地域的应用,使用ANSI类编码还是很节约空间的。打个比方在中,做中国的应用使用GBK就行。除非对方没有安装中文语言包,否则是可以显示的。但是他无乱是传输用到的带宽还是Database用的空间,都比UTF要解决三分之一左右。
1、Unicode
Unicode的中文名字很霸气——统一码、万国码、单一码。它为每种语言的每个字符设定了统一并且唯一的二进制码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode的编码形式是3个字节的,0-0x10FFFF。这里有个插曲,在制定该统一码的时候,还有个机构也在研究,就是ISO。制定出Unicode的是,统一码联盟。可喜的是,后来两者统一了。这里我们了解下,在ISO制定过程中出现的产物,USC-2,USC-4分别对应2个字节的编码和4个字节的编码。具体的内容这里就不详细说明了。作为统一的方式,就是ISO不会再扩展超过3个字节以上的字符编码编码方式了。目前实际应用的Unicode版本使用的16位的版本,对应于USC-2
但Unicode(和USC都)不适合传输,它是中定长的表示方法,很耗费资源。因为他没有明确的规定如何传输和存储。因为对于有些情况,比如低位的ASCII对应的字符,明明只要一个字节就可以保存。如果用3个字节也太浪费了。尤其当大量存在时,这个对网络带宽始终浪费。那么我们就需要有标记,标记哪里是尾部,哪里是首部。
于是UTF编码诞生了。UTF的全程式USC Transfer Format,如果想称为Unicode Transfer Format也问题不大。对于像对应的8,16,32。其实就是固定的编码大小。比如UTF-8是以1个字节作为最小的单位,UTF-16就是2个字节是最小的表示单位,UTF-32是以最小的表示单位为4个字节为最小单位的,这个也就是USC-4(基本不用了),这里也能看出是ISO提出的。既然统一了,那么通过增加Endian的方式来标记读取的方向,哪边是头哪边是尾。
这个有个概念叫BOM头。BOM头是用来在编码中规定方向的一种特殊编码,比如0XFFFF,0XFFFE分别对应BE(Big Endian)和LE(little Endian)(大尾和小尾)。
但是这个只是决定了读取方向的问题,还有个问题时何处是结束。这个就要使用到UTF中模板的概念了。
其中UTF-16和Unicode编码大致一样, UTF-8就是以8位为单元对Unicode进行编码。从Unicode到UTF-8的编码方式如下:
Unicode编码(16进制) UTF-8 字节流(二进制)
000000 -00 007F 0xxxxxxx
000080 – 0007FF 110xxxxx 10xxxxxx
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 -10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
例如“汉”字的Unicode编码是6C49。6C49在000800-00FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
他通过固定UTF-8中二进制字节流的基本形式,来进行处理。从上面的模板就可以看出,以0开头的肯定是1个字节的,以E打头的一定是3个字节的,其他的都是两个字节的。完美的完成了大小判断,配合方向,就知道每段是代表几个字节了。同时如果丢了个字节,从下个段开始又进行判断,该段式几个自己的。具有了非常好的容错。
扩展:
对于上面的模板知识,我们做进一步的研究。
上述机制,使得UTF8具有容错的能力,并且是一个动态大小的字节流,但是这样的机制会对本身UTF8容量造成影响,毕竟其中固定了一些位数。当然对于容量来说,只会对于高位的Unicode才会存在比Ansi更占空间的情况。这个从模板中可以看出来,在000000-0007FF的范围内,都是UTF-8的占用字节数是小于等于Unicode的,只有当高于这个范围时,才会出现UTF-8比Unicode费空间的情况,这也就是为什么UTF8对于英语网站来说会比用Unicode或Ansi更小的原因。
下面给出的列表,表示在一个文本中存入"1234567890"的内容,针对不同的编码,其大小也不同,这里主要比较UTF-8,Unicode,Ansi,GBK这4中形式
1、UTF-8 13BYTES Disk-4.096BTYPES
2、Unicode 22BYTES Disk -4.096BYTES
3、Ansi 10BTYES Disk-4.096 BYTES
4、GBK 13BYPTE Disk-40396BYTES
在来一组内容是“好人一生平安”的中文内容,这里中文的肯定是高位的情况
1、UTF-8 21BYTES Disk-4.096BTYPES
2、Unicode 14BYTES Disk -4.096BYTES
3、Ansi 12BTYES Disk-4.096 BYTES
4、GBK 12BTYES Disk-4.096 BYTES
这里要注意的是GBK其实是一种ANSI编码,当选择ANSI时候,他会自动选择最匹配的编码,比如输入中文的时候,选择ANSI他就会自动选择GB2312作为编码格式。
根据上面的情况,我们很容易发现,当存英文或数字的时候,UTF-8和GBK是一样的,这是因为他们都启用了,ASCII码的节约空间机制。而ANSI会比GBK等小的原因是,他有可能针对ASCII情况,进行了7位处理,比如像UTF-7的情况。对于中文的情况,因为他处于高位,所以可以发现其使用的空间情况比Unicode还多,这个就是有好有坏了。总之对于UTF-8来说对于英语国家很有利,对于中文国家。。上面的数据也说明了很不利。除非我们使用GBK.
----------------
character encoding
OK,让我们先看下场景1 从一个URL中看起: http://www.baidu.com/s?wd=%E6%88%91%E6%98%AF%E5%A5%BD%E4%BA%BA 肯定有人奇怪wd=后面一串是什么玩意? 你可以用JavaScript中的decodeUri解析出来: decodeURI("%E6%88%91%E6%98%AF%E5%A5%BD%E4%BA%BA") //输出 "我是好人" 这里,推荐你阅读下,这篇文章。在这篇文章中,你会知道,为什么用decodeURI可以解码出,为什么unescape不可以。 言归正传,这串奇怪的字符到底是怎么出来的呢? 其实就是将UTF-8版的“我是好人”,传入了encodeURI,然后得到的。 这里有两点要注意, 1、本身文字内容的编码是UTF-8 2、传入encodeURI,其实就等于又进行了一次urlencode 没有错,做了两种编码操作,这就是为什么这个代码看上去这么怪的原因。 ok,这里先说这个情况。接下来,让我们看场景2: escape("我是好人") //输出 "%u6211%u662F%u597D%u4EBA" 这一串又是什么?这个是unicode,其实真正的unicode码是6211,只是为了表示是unicode,js里面都会加上%u。 再看个场景3 将下面这段复制到Html文本中,然后用浏览器显示:
我是好人
你会看到,页面上显示“我是好人”。
我靠!这又是什么情况?
其实,你将场景2中的数字部分,转换成10进制,对应的就是场景3中的数字了,不同的是一个前面用的是%u,场景3中,用的是&#。
其实上面,还可以写成:
我是好人
效果是一样的。
但是&#可以直接被浏览器渲染,这是因为HTML是采用UTF-8作为标准字集。(注意,unicode的编码和UTF-8的编码只需要通过模板就可以很方便的转换,所以,从一定意义上可以认为unicode和utf-8是一回事,但是unicode和GBK完全不是一回事,这点要注意。)
这里其实就是&#nnn,这个的nnn就是unicode的编码,而&#可以认为是html的约定,只要出现这个就是需要转义的。
而%u只是转码出来用来标示这是unicode的,并不是用来告诉浏览器如何解析的。
现在让我们思考场景1和场景2的关系。
其实说到这里就要明白utf8产生的原因,其原因是因为要减少网络传输的使用带宽,同时前面也说utf和unicode的互转可以通过模板简单实现。同时,浏览器是可以识别utf的,当然你要指定,此外操作系统本身就可以识别unicode,自然浏览器也可以识别。所以,你直观看起来就可能比较迷惑了。因为都是“我是好人”。其实这个时候我们应该从他们对应的十六进制码来看,对于utf和unicode来说,两者对应的十六进制值是不同的。这点很关键,这也就是他的背后要两个不同函数解析的原因。总而言之,对于编码操作,为了不混淆,在思考问题时,一定要转换成对应的16进制值。--------------------
Urlencoding,ASCII,UNICODE,UTF8,GBK等编码详解
1、URL ENCODING
URLs只可以使用ASCII字符集在Internet上传输。
由于URLs经常会含有ASCII集之外的字符,此时URL就不得不将这些非ASCII的字符转换成有效的ASCII字符。
另外,URL encoding还会将不安全的ASCII字符使用以“%”开头后面紧跟两个十六进制数的形式,进行替换。
注意:
URLs是不能包含空格的。URL encoding一般会使用“+”号去替换空格。当然,也可以使用%20,这两种其实是一样的,只是一般前者是使用如下代码会得到
<form name="input" target="_blank" action="html_form_submit.asp" method="get">
<input type="text" value="Hello Günter" name="text" size="30">
<input type="submit" value="Submit">
</form>
当在input中填入一个空格,然后点击Submint时,会自动跳转到URL:“/html_form_submit.asp?text=+”,注意此处的空格变成了+
当使用JavaScript的escape()函数进行处理的时候,便会将空格处理成%20
重要解释:
其实URL encoding对于整个ascii都进行了编码,比如A为%41,B为%42,但是并一定会用到。因为URL本身就可以传输ascii,在前面也说了,有必要替换的无非也就是不安全的一些ascii数值,比如[、<之类的。那既然如此为什么还要对A等等进行编码呢?其实,这里应该抽象出去看。说到底,ASCII也就是一些二进制数(或说是十六进制数),他现有这些数值,然后让A,B去对应了这些值而已。这样的话,当对于例如UTF的情况,中文为三个字节,就可以认为是3个ASCII组成,比如一个中文字我,对应的ASCII码,如果拆成单个字节的ASCII码看的话,正好分别是 æ^',但是这是没有意义的,所以对于不同的编码会如何确认多少个字节为一组,因此我们传过去最好的传的是特定的编码(此处我的URL encoding为%E6%88%91,%后的其实就是对应的编码,可以认为组成了一组编号。)当后端接收到该字符后,再用特定的函数,可以对这些进行过URL encoding处理的字符串,再所反向处理。这也就是为什么这些反向处理函数需要选择当前传入的这些字符串是UTF8还是GBK,不同的语言决定了如何分段这些编码。这些处理程序,会把被URL encoding处理过的字符,根据指定的编码格式,然后解码出来。显示成我们人能读懂的格式。
所以这里看到的E6%88%91,你也可以认为是机器码,不同的机器码对应了不同的自然界符号。当然这些定义是我们人规定的。
1、ASCII
ASCII字符集被用于通过Internet在计算机间传递信息。ASCII是American Standard Code for Information Interchange的缩写。设计于60年代,并作为一种标准字符集广泛应用于计算机和其他硬件设备中。
ASCII是7位的字符集含有128字符,注意后来因为发展的需要,不够表示了,符号多了,于是将ASCII扩展成了8位的,正好一个字节,可以代表256个字符。扩展的主要是些全球其他国家比较通用的符号,比如£这个英镑符号,¥人民币符号,这些在最原始的ASCII码中是不包括的。当然这个改变后的ASCII不再叫ASCII,而是ISO 8859-1,又因为里面新增的字符时拉丁语,所以又成Latin-1.这就是著名的Latin编码方式。
这里主要说下原始的ASCII,其中主要包括了0-9的数字,a-z的字母(包括大写和小写)和一些特殊字符。
ASCII字符集广泛应用于现代计算机中,HTML,Internet都是基于ASCII的。
注意,这里有个很重要的理解,就是什么才是编码,是二进制或十六进制数才是编码,其对应的符号,只是图像。编码配合解码函数-》可读的符号。所以,要清楚编码是进制数。
这里着重讲下HTML Entity Code。在这个概念中有两个概念,一个事Entity Name,比如 ©s;等等,还有个是Entity Number,比如 ,©和前面的Entity Name是对应关系。为什么要引入HTML Entity Code呢?你想想,我们现在键盘能够表示键入的符号有多少个呢?你能在键盘上找到版权的符号吗?你是找不到的,但是明明ASCII中有,那么我们怎么才能输入呢。。。那么就是转码,就是用键盘可以输入的字符,组成一定的组合变成,键盘无法输入但是我们需要且ASCII中有的符号。OK。就这样。HTML Entity Code诞生了。那么Name和Number什么区别呢?就是两种表示方式而已。之所以没有用ASCII码直接显示,而是要再使用ASCII中转下的再显示的原因。主要还是我们的键盘。。表现力有限。如果可以把整个ASCII都能从键盘输入。那么也就没有必要这么中转了。
1、ANSI
这个编码的出现,主要是因为需求的不断扩展,需要表示的符号,不断增多。但是其代表的是一种编码方式。即使用2个字节代表一个字符的延伸编码方式。这个方式没有没有严格的标准或规则去统揽全局。。。于是乎,编码的世界进入了一篇混沌。比方说,在简体中文系统中,ANSI编码代表GB2312编码,日文系统中ANSI编码代表JIS编码。不同的ANSI编码是不兼容的,想想也知道。。。肯定是由冲突的。相同的编码对应着不同的字符。这下就麻烦了。。。。不同的编码,如果使用两种不同的不同的编码方式,肯定显示不一样。。。,这就出现个问题,中文系统的东西。。日文系统看过去就是一堆乱码。。我如果想再日文系统中能显示中文,该怎么做呢?除非同时安装有日文语言包和中文语言包,否则很遗憾。。。没有办法。。。于是乎,为了解决这个问题,我需要在日文系统中能显示中文,那么我就需要一个统一编码方式,里面不仅含有中文的编码,还有日文的编码。。。所以,扩大编码的格式是必须的了。同时还有个更麻烦的问题,在同一套系统中如果处理多套的编码格式。。。OMG,你的处理程序估计就很混乱了。因此出现了一统江湖的Unicode。
当然ANSI并不是一无是处,对于强地域的应用,使用ANSI类编码还是很节约空间的。打个比方在中,做中国的应用使用GBK就行。除非对方没有安装中文语言包,否则是可以显示的。但是他无乱是传输用到的带宽还是Database用的空间,都比UTF要解决三分之一左右。
1、Unicode
Unicode的中文名字很霸气——统一码、万国码、单一码。它为每种语言的每个字符设定了统一并且唯一的二进制码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode的编码形式是3个字节的,0-0x10FFFF。这里有个插曲,在制定该统一码的时候,还有个机构也在研究,就是ISO。制定出Unicode的是,统一码联盟。可喜的是,后来两者统一了。这里我们了解下,在ISO制定过程中出现的产物,USC-2,USC-4分别对应2个字节的编码和4个字节的编码。具体的内容这里就不详细说明了。作为统一的方式,就是ISO不会再扩展超过3个字节以上的字符编码编码方式了。目前实际应用的Unicode版本使用的16位的版本,对应于USC-2
但Unicode(和USC都)不适合传输,它是中定长的表示方法,很耗费资源。因为他没有明确的规定如何传输和存储。因为对于有些情况,比如低位的ASCII对应的字符,明明只要一个字节就可以保存。如果用3个字节也太浪费了。尤其当大量存在时,这个对网络带宽始终浪费。那么我们就需要有标记,标记哪里是尾部,哪里是首部。
于是UTF编码诞生了。UTF的全程式USC Transfer Format,如果想称为Unicode Transfer Format也问题不大。对于像对应的8,16,32。其实就是固定的编码大小。比如UTF-8是以1个字节作为最小的单位,UTF-16就是2个字节是最小的表示单位,UTF-32是以最小的表示单位为4个字节为最小单位的,这个也就是USC-4(基本不用了),这里也能看出是ISO提出的。既然统一了,那么通过增加Endian的方式来标记读取的方向,哪边是头哪边是尾。
这个有个概念叫BOM头。BOM头是用来在编码中规定方向的一种特殊编码,比如0XFFFF,0XFFFE分别对应BE(Big Endian)和LE(little Endian)(大尾和小尾)。
但是这个只是决定了读取方向的问题,还有个问题时何处是结束。这个就要使用到UTF中模板的概念了。
其中UTF-16和Unicode编码大致一样, UTF-8就是以8位为单元对Unicode进行编码。从Unicode到UTF-8的编码方式如下:
Unicode编码(16进制) UTF-8 字节流(二进制)
000000 -00 007F 0xxxxxxx
000080 – 0007FF 110xxxxx 10xxxxxx
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 -10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
例如“汉”字的Unicode编码是6C49。6C49在000800-00FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
他通过固定UTF-8中二进制字节流的基本形式,来进行处理。从上面的模板就可以看出,以0开头的肯定是1个字节的,以E打头的一定是3个字节的,其他的都是两个字节的。完美的完成了大小判断,配合方向,就知道每段是代表几个字节了。同时如果丢了个字节,从下个段开始又进行判断,该段式几个自己的。具有了非常好的容错。
扩展:
对于上面的模板知识,我们做进一步的研究。
上述机制,使得UTF8具有容错的能力,并且是一个动态大小的字节流,但是这样的机制会对本身UTF8容量造成影响,毕竟其中固定了一些位数。当然对于容量来说,只会对于高位的Unicode才会存在比Ansi更占空间的情况。这个从模板中可以看出来,在000000-0007FF的范围内,都是UTF-8的占用字节数是小于等于Unicode的,只有当高于这个范围时,才会出现UTF-8比Unicode费空间的情况,这也就是为什么UTF8对于英语网站来说会比用Unicode或Ansi更小的原因。
下面给出的列表,表示在一个文本中存入"1234567890"的内容,针对不同的编码,其大小也不同,这里主要比较UTF-8,Unicode,Ansi,GBK这4中形式
1、UTF-8 13BYTES Disk-4.096BTYPES
2、Unicode 22BYTES Disk -4.096BYTES
3、Ansi 10BTYES Disk-4.096 BYTES
4、GBK 13BYPTE Disk-40396BYTES
在来一组内容是“好人一生平安”的中文内容,这里中文的肯定是高位的情况
1、UTF-8 21BYTES Disk-4.096BTYPES
2、Unicode 14BYTES Disk -4.096BYTES
3、Ansi 12BTYES Disk-4.096 BYTES
4、GBK 12BTYES Disk-4.096 BYTES
这里要注意的是GBK其实是一种ANSI编码,当选择ANSI时候,他会自动选择最匹配的编码,比如输入中文的时候,选择ANSI他就会自动选择GB2312作为编码格式。
根据上面的情况,我们很容易发现,当存英文或数字的时候,UTF-8和GBK是一样的,这是因为他们都启用了,ASCII码的节约空间机制。而ANSI会比GBK等小的原因是,他有可能针对ASCII情况,进行了7位处理,比如像UTF-7的情况。对于中文的情况,因为他处于高位,所以可以发现其使用的空间情况比Unicode还多,这个就是有好有坏了。总之对于UTF-8来说对于英语国家很有利,对于中文国家。。上面的数据也说明了很不利。除非我们使用GBK.
----------------
character encoding
介绍字符编码的一些基本概念,以及unicode和utf-8, utf-16, utf-32的基本知识。
譬如unicode字符集,便包含了地球上大部分的自然语言字符。
譬 如,在编码字符集ISO 8859-1中,code point 233代表字符’é’,但在编码字符集ISO 8859-5中,code point 233代表的是字符’щ’。而在unicode编码字符集同时收录了这两个字符,分别用code point 233, 1097代表。
utf-8将比特流逐个字节解析,且解析出的字节可分成single bytes, leading bytes, continuation bytes三种。这三种字节表示的数值范围没有交集,因而可立即判断该如何解析。以下是更具体的特征:
unicode,ansi,utf-8,unicode big endian编码的区别
概念
character sets
一组特定用途的字符形成的集合便是一个字符集(character set)。譬如简体汉字、英文、阿拉伯语等。当然,一个字符集中的字符不限于一门自然语言,可以包含各种字符。譬如unicode字符集,便包含了地球上大部分的自然语言字符。
coded character sets
为了方便计算机处理,通常将一个字符集中所有的字符都一一映射到(编码成)一个数字(code point)。字符集对应的code point集合便是编码字符集(coded character set)。譬 如,在编码字符集ISO 8859-1中,code point 233代表字符’é’,但在编码字符集ISO 8859-5中,code point 233代表的是字符’щ’。而在unicode编码字符集同时收录了这两个字符,分别用code point 233, 1097代表。
character encodings
数 字在计算机底层可以用多种方式表示。譬如,可用1位’1’表示数字1,也可用两位’01’表示数字1.这个映射,便是字符编码(encoding),即将 一个code point编码成一串0, 1。unicode通常的编码方式是utf-8,utf-16,utf-32。unicode
code point是一个4到6位的16进制数,以”U+”开头。例如:- ASCII whitespace: U+0009, U+000A, U+000C, U+000D, and U+0020
- ASCII digits: U+0030 to U+0039
- A-Z: U+0041 to U+005A
- a-z: U+0061 to U+007A
utf-8
uft-8是一种变长的字符编码,即每一个code point在计算机里面的表示长度都不尽相同。具体见下表。Bits | lowest | highest | Bytes | 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+1FFFF | 4 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | ||
26 | U+200000 | U+3FFFFF | 5 | 111110xx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | |
31 | U+4000000 | U+7FFFFF | 6 | 1111110x | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
- ASCII中0到127对应的字符用single bytes,以0开头
- 高于127的code points用多个字节表示,首字节为leading byte, 其余为continuation byte
- leading byte高位上连续有两个以上的1,且其数目与该编码使用的字节数一样
- continuation byte高位为10
utf-16
与utf-8一样,是变长编码。常见的code point都用16位进行编码,其它的可能会占用更多的字节。见维基百科。utf-32
与utf-8和utf-16不一样,是定长编码,所有code point都用32位进行编码。见维基百科。How to choose a character encoding?
要想浏览器能正确解码收到的字节流,需要在编辑时将数据保存为一种编码A,服务器在提供内容时在header中指定文件类型(Content-Type)。都用utf-8可以保持最大的兼容性。推荐教程
- http://www.w3.org/International/tutorials/tutorial-char-enc/
- http://www.w3.org/International/questions/qa-what-is-encoding
- http://www.w3.org/International/questions/qa-choosing-encodings
- http://www.w3.org/International/articles/definitions-characters/
更多参考
- http://encoding.spec.whatwg.org/
- http://www.unicode.org/versions/Unicode6.3.0/
- http://www.unicode.org/standard/principles.html
utf-8 related
- http://www.ietf.org/rfc/rfc3629.txt
- http://en.wikipedia.org/wiki/Utf-8
- http://www.cl.cam.ac.uk/~mgk25/ucs/ISO-10646-UTF-8.html
- http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
more
Tools
---------------
unicode,ansi,utf-8,unicode big endian编码的区别
很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为”字节“。再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为”计算机“。
开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。
他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上0×10,
终端就换行,遇上0×07, 终端就向人们嘟嘟叫,例好遇上0x1b,
打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0×20以下的字节状态称为”控制码”。他们又把所有的空
格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉
很好,于是大家都把这个方案叫做 ANSI 的”Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。
他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上0×10,
终端就换行,遇上0×07, 终端就向人们嘟嘟叫,例好遇上0x1b,
打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0×20以下的字节状态称为”控制码”。他们又把所有的空
格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉
很好,于是大家都把这个方案叫做 ANSI 的”Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。
后来,就像建造巴比伦塔一样,世界各地的都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机
保存他们的文字,他们决定采用
127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128
到255这一页的字符集被称”扩展字符集“。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧!
保存他们的文字,他们决定采用
127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128
到255这一页的字符集被称”扩展字符集“。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧!
等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。但是这难不倒智慧的中国人民,我们不客气
地把那些127号之后的奇异符号们直接取消掉,
规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到
0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的
字母、日文的假名们都编进去了,连在 ASCII
里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。
中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。
地把那些127号之后的奇异符号们直接取消掉,
规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到
0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的
字母、日文的假名们都编进去了,连在 ASCII
里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。
中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。
但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把
GB2312 没有用到的码位找出来老实不客气地用上。
后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字
符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。 中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS“(Double
Byte Charecter Set
双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处
理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣
们都要每天念下面这个咒语数百遍: “一个汉字算两个英文字符!一个汉字算两个英文字符……”
GB2312 没有用到的码位找出来老实不客气地用上。
后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字
符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。 中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS“(Double
Byte Charecter Set
双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处
理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣
们都要每天念下面这个咒语数百遍: “一个汉字算两个英文字符!一个汉字算两个英文字符……”
因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海
里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS
编码方案——当时的中国人想让电脑显示汉字,就必须装上一个”汉字系统”,专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序
就必须加装另一套支持 BIG5
编码的什么”倚天汉字系统”才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎
么办? 真是计算机的巴比伦塔命题啊!
里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS
编码方案——当时的中国人想让电脑显示汉字,就必须装上一个”汉字系统”,专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序
就必须加装另一套支持 BIG5
编码的什么”倚天汉字系统”才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎
么办? 真是计算机的巴比伦塔命题啊!
正在这时,大天使加百列及时出现了——一个叫 ISO
(国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号
的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。
(国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号
的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。
unicode开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO
就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,unicode包持其原编码不变,只是将其长度由原
来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在
保存英文文本时会多浪费一倍的空间。
就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,unicode包持其原编码不变,只是将其长度由原
来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在
保存英文文本时会多浪费一倍的空间。
这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的strlen函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是的,从unicode开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的”一个字符“!同时,也都是统一的”两个字节“,请注意”字符”和”字节”两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在unicode中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。
unicode同样也不完美,这里就有两个的问题,一个是,如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是
分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每
个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费,文本文件的大小会因此大出二三倍,这是难以接受的。
分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每
个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费,文本文件的大小会因此大出二三倍,这是难以接受的。
unicode在很长一段时间内无法推广,直到互联网的出现,为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII
码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中
文字符占3个字节)。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换。
码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中
文字符占3个字节)。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换。
Unicode符号范围 | UTF-8编码方式(十六进制) | (二进制)
—————————————————————–
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
总结一下
8bit表示1个字节 —> 256个ASCII码(ANSI-美国国家标准学会) 英语够用了
中国人也要用计算机,所以有了GB2312 , 小于127的编码保持原意,但两个大于127的字符连在一起时,就表示一个汉字.这里面包括两个字节组成的标点符号,比如我们常说的全角逗号,句号. 后来还是不够用又扩展出了GBK,GB18030.
外国人也要用计算机,一个叫 ISO (国际标谁化组织)就搞出了UCS编码(Universal Multiple-Octet Coded Character Set),俗称Unicode.
为解决unicode如何在网络上传输,又出现了UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII
码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中文字符占3个字节)。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换.
码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中文字符占3个字节)。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换.