今天中午Q我H然x(chng)清楚Unicode和UTF-8之间的关p,于是开始在|上查资料?/span>
l果Q这个问题比我想象的复杂Q从午饭后一直看到晚?点,才算初步搞清楚?/span>
下面是我的W记Q主要用来整理自q思\。但是,我尽量试囑ֆ得通俗易懂Q希望能对其他朋友有用。毕竟,字符~码是计机技术的基石Q想要熟l用计机Q就必须懂得一点字W编码的知识?/span>
1. ASCII?/strong>
我们知道Q在计算机内部,所有的信息最l都表示Z个二q制的字W串。每一个二q制位(bitQ有0?两种状态,因此八个二进制位可以组合出256U状态,q被UCؓ(f)一个字节(byteQ。也是_(d)一个字节一共可以用来表C?56U不同的状态,每一个状态对应一个符P是256个符P?000000?1111111?/span>
上个世纪60q代Q美国制定了(jin)一套字W编码,对英语字W与二进制位之间的关p,做了(jin)l一规定。这被称为ASCII码,一直沿用至今?/span>
ASCII码一p定了(jin)128个字W的~码Q比如空?#8220;SPACE”?2Q二q制00100000Q,大写的字母A?5Q二q制01000001Q。这128个符P包括32个不能打印出来的控制W号Q,只占用了(jin)一个字节的后面7位,最前面?位统一规定??/span>
2、非ASCII~码
p?28个符L(fng)码就够了(jin)Q但是用来表C其他语aQ?28个符h不够的。比如,在法语中Q字母上Ҏ(gu)注音W号Q它?yu)无法用ASCII码表C。于是,一些欧z国家就军_Q利用字节中闲置的最高(sh)~入新的W号。比如,法语中的é的编码ؓ(f)130Q二q制10000010Q。这样一来,q些Ƨ洲国家使用的编码体p,可以表示最?56个符受?/span>
但是Q这里又出现?jin)新的问题。不同的国家有不同的字母Q因此,哪怕它们都使用256个符L(fng)~码方式Q代表的字母却不一栗比如,130在法语编码中代表?#233;Q在希伯来语~码中却代表?jin)字母Gimel (ג)Q在俄语~码中又?x)代表另一个符受但是不怎样Q所有这些编码方式中Q?—127表示的符h一L(fng)Q不一L(fng)只是128—255的这一Dc(din)?/span>
至于亚洲国家的文字,使用的符号就更多?jin),汉字多?0万左叟뀂一个字节只能表C?56U符P肯定是不够的Q就必须使用多个字节表达一个符受比如,体中文常见的~码方式是GB2312Q用两个字节表CZ个汉字,所以理Z最多可以表C?56x256=65536个符受?/span>
中文~码的问题需要专文讨论,q篇W记不涉?qing)。这里只指出Q虽焉是用多个字节表示一个符P但是GBcȝ汉字~码与后文的Unicode和UTF-8是毫无关pȝ?/span>
3.Unicode
正如上一节所_(d)世界上存在着多种~码方式Q同一个二q制数字可以被解释成不同的符受因此,要想打开一个文本文Ӟ必ȝ道它的编码方式,否则用错误的~码方式解读Q就?x)出Cؕ码。ؓ(f)什么电(sh)子邮件常常出Cؕ码?是因ؓ(f)发信人和收信Z用的~码方式不一栗?/span>
可以惌Q如果有一U编码,世界上所有的W号都纳入其中。每一个符号都l予一个独一无二的编码,那么q问题׃(x)消失。这是UnicodeQ就像它的名字都表示的,q是一U所有符L(fng)~码?/span>
Unicode当然是一个很大的集合Q现在的规模可以容纳100多万个符受每个符L(fng)~码都不一P比如QU+0639表示阿拉伯字母AinQU+0041表示p的大写字母AQU+4E25表示汉字“?#8221;。具体的W号对应表,可以查询unicode.orgQ或者专门的汉字对应?/span>?/span>
4. Unicode的问?/strong>
需要注意的是,Unicode只是一个符号集Q它只规定了(jin)W号的二q制代码Q却没有规定q个二进制代码应该如何存储?/span>
比如Q汉?#8220;?#8221;的unicode是十六进制数4E25Q{换成二进制数?5位(100111000100101Q,也就是说q个W号的表C需?个字节。表C其他更大的W号Q可能需?个字节或?个字节,甚至更多?/span>
q里有两个严重的问题,W一个问题是Q如何才能区别unicode和asciiQ计机怎么知道三个字节表示一个符P而不是分别表CZ个符号呢Q第二个问题是,我们已经知道Q英文字母只用一个字节表C就够了(jin)Q如果unicodel一规定Q每个符L(fng)三个或四个字节表C,那么每个英文字母前都必然有二C个字节是0Q这对于存储来说是极大的费Q文本文件的大小?x)因此大Z三倍,q是无法接受的?/span>
它们造成的结果是Q?Q出C(jin)unicode的多U存储方式,也就是说有许多种不同的二q制格式Q可以用来表Cunicode?Qunicode在很长一D|间内无法推广Q直C联网的出现?/span>
5.UTF-8
互联|的普及(qing)Q强烈要求出CU统一的编码方式。UTF-8是在互联网上用最q的一Uunicode的实现方式。其他实现方式还包括UTF-16和UTF-32Q不q在互联|上基本不用?/span>重复一遍,q里的关pLQUTF-8是Unicode的实现方式之一?/strong>
UTF-8最大的一个特点,是它是一U变长的~码方式。它可以使用1~4个字节表CZ个符PҎ(gu)不同的符可(g)变化字节长度?/span>
UTF-8的编码规则很单,只有二条Q?/span>
1Q对于单字节的符P字节的第一位设?Q后?位ؓ(f)q个W号的unicode码。因此对于英语字母,UTF-8~码和ASCII码是相同的?/span>
2Q对于n字节的符Pn>1Q,W一个字节的前n位都设ؓ(f)1Q第n+1位设?Q后面字节的前两位一律设?0。剩下的没有提及(qing)的二q制位,全部个符L(fng)unicode码?/span>
下表ȝ?jin)编码规则,字母x表示可用~码的位?/span>
UnicodeW号范围 | UTF-8~码方式
(十六q制) | Q二q制Q?/span>
--------------------+---------------------------------------------
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
下面Q还是以汉字“?#8221;ZQ演C如何实现UTF-8~码?/span>
已知“?#8221;的unicode?E25Q?00111000100101Q,Ҏ(gu)上表Q可以发?E25处在W三行的范围内(0000 0800-0000 FFFFQ,因此“?#8221;的UTF-8~码需要三个字节,x(chng)式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,?#8220;?#8221;的最后一个二q制位开始,依次从后向前填入格式中的xQ多出的位补0。这样就得到?jin)?#8220;?#8221;的UTF-8~码?#8220;11100100 10111000 10100101”Q{换成十六q制是E4B8A5?/span>
6. Unicode与UTF-8之间的{?/strong>
通过上一节的例子Q可以看?#8220;?#8221;的Unicode码是4E25QUTF-8~码是E4B8A5Q两者是不一L(fng)。它们之间的转换可以通过E序实现?/span>
在Windowsq_下,有一个最单的转化Ҏ(gu)Q就是用内|的C本小E序Notepad.exe。打开文g后,点击“文g”菜单中的“另存?sh)?#8221;命o(h)Q会(x)跛_一个对话框Q在最底部有一?#8220;~码”的下拉条?/span>
里面有四个选项QANSIQUnicodeQUnicode big endian ?UTF-8?/span>
1QANSI是默认的~码方式。对于英文文件是ASCII~码Q对于简体中文文件是GB2312~码Q只针对Windows体中文版Q如果是J体中文版会(x)采用Big5码)(j)?/span>
2QUnicode~码指的是UCS-2~码方式Q即直接用两个字节存入字W的Unicode码。这个选项用的little endian格式?/span>
3QUnicode big endian~码与上一个选项相对应。我在下一节会(x)解释little endian和big endian的涵义?/span>
4QUTF-8~码Q也是上一节谈到的~码Ҏ(gu)?/span>
选择?#8221;~码方式“后,点击”保存“按钮Q文件的~码方式q刻{换好?jin)?/span>
7. Little endian和Big endian
上一节已l提刎ͼUnicode码可以采用UCS-2格式直接存储。以汉字”?#8220;ZQUnicode码是4E25Q需要用两个字节存储Q一个字节是4EQ另一个字节是25。存储的时候,4E在前Q?5在后Q就是Big endian方式Q?5在前Q?E在后Q就是Little endian方式?/span>
q两个古怪的名称来自英国作家斯威夫特的《格列佛(jng)游记》。在该书中,h国里爆发?jin)内战,战?sh)起因是h们争论,吃鸡蛋时I竟是从大头(Big-Endian)敲开q是从小?Little-Endian)敲开。ؓ(f)?jin)这件事情,前后爆发了(jin)六ơ战(sh),一个皇帝送了(jin)命,另一个皇帝丢?jin)王位?/span>
因此Q第一个字节在前,是”大头方式“QBig endianQ,W二个字节在前就?#8221;头方式“QLittle endianQ?/span>
那么很自然的Q就?x)出C个问题:(x)计算机怎么知道某一个文件到底采用哪一U方式编码?
Unicode规范中定义,每一个文件的最前面分别加入一个表C编码顺序的字符Q这个字W的名字叫做”零宽度非换行I格“QZERO WIDTH NO-BREAK SPACEQ,用FEFF表示。这正好是两个字节,而且FF比FE??/span>
如果一个文本文件的头两个字节是FE FFQ就表示该文仉用大头方式;如果头两个字节是FF FEQ就表示该文仉用小头方式?/span>
8. 实例
下面QD一个实例?/span>
打开”C?#8220;E序Notepad.exeQ新Z个文本文Ӟ内容是一?#8221;?#8220;字,依次采用ANSIQUnicodeQUnicode big endian ?UTF-8~码方式保存?/span>
然后Q用文本~辑软gUltraEdit?/span>?#8221;十六q制功能“Q观察该文g的内部编码方式?/span>
1QANSIQ文件的~码是两个字节“D1 CF”Q这正是“?#8221;的GB2312~码Q这也暗CGB2312是采用大头方式存储的?/span>
2QUnicodeQ编码是四个字节“FF FE 25 4E”Q其?#8220;FF FE”表明是小头方式存储,真正的编码是4E25?/span>
3QUnicode big endianQ编码是四个字节“FE FF 4E 25”Q其?#8220;FE FF”表明是大头方式存储?/span>
4QUTF-8Q编码是六个字节“EF BB BF E4 B8 A5”Q前三个字节“EF BB BF”表示q是UTF-8~码Q后三个“E4B8A5”是“?#8221;的具体编码,它的存储序与编码顺序是一致的?/span>