Read Sean

Read me, read Sean.
posts - 508, comments - 655, trackbacks - 9, articles - 4

本来是手写的一张草稿,清理台面的时候,正要扔,发现内容还有点意思,干脆吧,开个随笔记录一下,备忘,说不定还能帮到别人。

在我们日常生活和工作中,尤其浏览含有中文的网站,时常会为乱码问题犯愁,一些天生Unicode支持不到位的编程语言和软件更是加重了这个现象。虽说已经是2009年了,我们时不时还是能碰到一些明显脑残的code,吐出一堆乱码,不论你用选什么字符集,似乎都无法还原最初的中文。比如"å·²",或者同一个页面,无法用同一个字符集显示,任何一种字符都至少有一部分显示不正确,不是这儿就是那儿。

首先科普一下UTF-8。UTF-8是Unicode所有字符编码实现中,应用范围最广的一个,最大的特点是兼容ASCII码,在此基础上,通过1..4个byte来表示每一个Unicode字符。它是怎么做到的?其实很简单,看首个byte:
00000000 ~ 01111111 : 0~127 (ASCII, 单个byte) 表示Unicode范围\u0000 ~ \u007F
11000000 ~ 11011111 : (2个1打头,所以2个byte) 表示Unicode范围\u0080 ~ \u07FF
11100000 ~ 11101111 : (3个1打头,所以3个byte) 表示Unicode范围\u0800 ~ \uFFFF
11110000 ~ 11110111 : (4个1打头,所以4个byte) 表示Unicode范围\u10000 ~ \u10FFFF

除了单个byte这个case,其他情况下,后续的byte均以10打头。这些打头的bit:10、110、1110、11110,都不作为具体编码的一部分参与真正Unicode编码的计算。所以1..4个byte,其有效位数分别为:7、11、16、21,因此才有了\u007F、\u07FF、\uFFFF这样的临界值,且4个byte的空间还有富余。

有了这个基础知识,我们就来具体看看这个"已"字,是怎么被UTF-8表示的,以及为什么处理不当的代码会最终输出"å·²"。

"已"字,用Unicode表示法,是\u5DF2,按照2个byte拆开成二进制表示:
01011101 11110010
如果用UTF-8编码,采用1110???? 10?????? 10??????这个格式,?号部分填入上述01011101 11110010,得到
11100101 10110111 10110010 这样3个byte。

好了,这个时候脑残代码出现,假如它不认识UTF-8,那么他会看到这样3个前后没有关联的byte,在西欧Latin-1字符集(即ISO 8859-1)中,它们分别表示"å"、"·"、"²"这3个字符。如果把它们分别再按照UTF-8编码,就成了:
11000011 10100101 11000010 10110111 11000010 10110010
完美的UTF-8编码,只不过,这完全是假象,只能通过非常规手段才能恢复它原本的样子。



只有注册用户登录后才能发表评论。


网站导航: