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