Chong Tang's Blog » 每一个软件开发人员{jd1}必须掌握的关于Unicode 和 ...
关键字:?????Unicode,?Character?Set,?字符集,?UTF-8,?ANSI,?ASCII,?UTF-7

原文标题:????The?Absolute?Minimum?Every?Software?Developer?Absolutely,?Positively?Must?Know

About?Unicode?and?Character?Sets(No?Excuses!)

原文链接:????http://www.joelonsoftware.com/printerFriendly/articles/Unicode.html

作者:???????Joel?Spolsky

来源:http://blog.csdn.net/inelm/ 作者:Joel?Spolsky

ASCII?码

————————————————————————————

7?位(00~7F)。?32?~?127?表示字符。32?是空格,?32?以下是控制字符(不可见)。

第8位没有被使用。全世界很多人同时对这个位的含义发展了不同的用处。比如?IBM?PC?中的?OEM?字符集。

{zh1}就?128?位以下的用处达成共识,制定了?ASCII?标准。

而?128?位以上的可能有不同的解释,这些不同的解释就叫做?code?pages.

甚至有用于在同一台电脑上解释多种语言的?code?page.

同时,在亚洲发生了更加疯狂的事情。亚洲语言的字符集通常数以千计,?8?位已经不足以表达,这通常用一种

很凌乱的,叫做?DBCS(双字节字符集,double?byte?character?set)?的系统来解决。

这种系统中,有些字符占用?1?字节,有些?2?字节。这样一来,在字符串中向前解析很容易,而倒退却很麻烦。

程序员们被建议,不要使用?s++?或?s–?来前进和后退,而使用一些函数,比如?Windows?的?AnsiNext?和

AnsiPrev.?因为这些函数知道是怎么回事。

这些不同的假设(code?page)在单个的机器上没有问题。而随着?Internet?的发展,字符串要从一个机器上移到

另一个机器上,这就产生了问题。于是,?Unicode?出现了。

Unicode

—————————————————————————————

Unicode?是一个勇敢的成就。它把在这个星球上的每一个合理的文字系统整合成了一个单一的字符集。

很多人还存在这样的误解:?Unicode?仅仅是?16?位的这么简单,每个字符占?16?位,所以一共有?65536?个可能的字符。

然而,这是错误的。不过不要紧,因为这是大部分人都会犯的一个普遍的错误。

实际上,Unicode?理解字符的方式是截然不同的,而这是我们必须了解的。

到目前为止,我们都曾经认为:一个字符对应到一些在磁盘上或内存中储存的位(bits).?如:?A?->?0100?0001

而在?Unicode?中,?一个字符实际上对应一种叫做?code?point?的东西。

比如?A?这个字符,是抽象的(原文:platonic,柏拉图式的,理想的)一个概念。

无论是?Times?New?Roman?或者?Helvetica?或者其他的什么字体中,都代表同一个字符。但是它和小写的字母?a?不同。

但是在其他的语言,比如希伯莱语(Hebrew)?或者德语(German),?阿拉伯语(Arabian)?中,同一个字母的不同的字形代表的含义是否

相同,是有争议的。经过长时间的争论,这些也终于被确定了。

每一个字母表中的每一个抽象的字母,都被赋予了一个数字,比如?U+0645.?这个叫做?code?point.

U+?表示:?Unicode,?数字是?16?进制的。

你可以通过?charmap?命令来查看所有这些编码。(Windows?2000/XP?中).?或者访问?Unicode?的网站(http://www.unicode.org)

Unicode?中?code?point?的数字的大小是没有限制的,而且也早就超过了?65535.?所以不是每个字符都能存储在两个字节中。

那么,一个字符串?”Hello”,?在?Unicode?中会表示成?5?个?code?points?:

U+0048?U+0065?U+006C?U+006C?U+006F

只不过是一些数字。但我们现在还没有提到如何在磁盘或者?Email?中表示这些信息,这就是我们下面要提到的编码(Encoding)?干的事情。

Encodings?(编码)

————————————————————————-

最初的?Unicode?Encoding,?使用两个字节表示一个字符。那么?”Hello”?表示为:

00?48?00?65?00?6C?00?6C?00?6F

实际上,还有一种表示方式:

48?00?65?00?6C?00?6C?00?6F?00

到底高位字节在前还是低位字节在前面,是两种不同的模式。这要看特定的?CPU?在何种模式下工作的更快。?所以这两种都有。

这就有了两种不同的?Unicode?表示方式了,为了区分,人们又采用了一种奇异的方式:

在每一个?Unicode?字符串的前面,加上?FEFF?(这称为?Unicode?字节顺序标志,Unicode?Byte?Order?Mark).

如果你交换高位和低位次序,那么会加上一个?FFFE.?这样,读这个字符串的人才知道要对每两个相邻的字节进行交换。

但在最初的时候,并不是每一个?Unicode?字符串都有这个标志的。

这看起来很不错。可程序员们开始抱怨了,“看看那些零!”。因为有些是美国人,他们使用英语。而英语中很少需要使用?U+00FF?以上的

字符,?有些人无法忍受采用双倍的存储空间来存储每个字符。

基于这些原因,很多人决定忽视?Unicode,?而同时,事情变得更糟了。

然后人们制定了?UTF-8.?UTF-8?是用于保存?Unicode?code?points?的另一套系统。

每一个?U+?数字,在内存中占用?8?bit.?在?UTF-8?中,任何一个?0~127?的?code?point?占用一个字节。

只有?128?以及更大的才占用?2,?3,?直到?6?个字节。

具体如下图所示:

16进制的最小的数????16进制的{zd0}的数???内存中的字节序列

——————————————————————————

00000000??????????0000007F?????????0vvvvvvv

00000080??????????000007FF?????????110vvvvv?10vvvvvv

00000800??????????0000FFFF?????????1110vvvv?10vvvvvv?10vvvvvv

00010000??????????001FFFFF?????????11110vvv?10vvvvvv?10vvvvvv?10vvvvvv

00200000??????????03FFFFFF?????????111110vv?10vvvvvv?10vvvvvv?10vvvvvv?10vvvvvv

04000000??????????7FFFFFFF?????????1111110v?10vvvvvv?10vvvvvv?10vvvvvv?10vvvvvv?10vvvvvv

这看起来很不错,其中的英文字符和?ASCII?中一样。所以美国人根本没意识到有什么错误。只有世界上的其他国家需要使用高位的字节。

特别的,”Hello”?这个字符串,Unicode?code?point?为?U+0048?U+0065?U+006C?U+006C?U+006F,?会被存储为?48?65?6C?6C?6F。

和?ASCII,?ANSI,?以及在这个星球上的任何一个?OEM?的字符集中表示的含义都一样。

现在,如果你需要表示重音的字符,或者希腊语,你需要使用多个字节来表示一个?code?point.?但美国人不会介意这些。

(UTF-8?还有一个好处就是,老的字符串处理程序使用一个为?0?的字节来表示?null-terminator,?不会截断字符串)

到目前为止已经介绍了三种?Unicode?的表示方法:

传统的双字节表示方法,?称为?UCS-2(因为有?2?个字节)?或者?UTF-16(因为有?16?个位)

而且你还要搞清楚是高位在前的,还是高位在后的?UCS-2.

还有一种就是新的?UTF-8.?如果你的程序只使用英文的话,它仍然会工作正常。

实际上还有一堆的其他办法对?Unicode?进行编码:

有?UTF-7,这种编码方式大部分和?UTF-8?相同,但保证高位一定为?0.

所以如果你必须通过某种?Email?系统传送?Unicode,这些系统认为?7?位足够了,那使用?UTF-7?会正常。

还有?UCS-4,?储存每一个?code?point?为?4?个字节。它的优点是每一个字符都保存为同样长的。但很明显,缺点是浪费太多存储空间了。

所以,现在你思考问题要把每一个字符想象成抽象的一个?unicode?code?point.?而它们同样可以使用任何旧的方式编码。

举例来说,你可以把?Unicode?字符串?Hello?(U+0048?U+0065?U+006C?U+006C?U+006F)?编码(encode)为

ASCII,?或者古老的?OEM?希腊语编码,或者希柏莱?ANSI?编码,等等。而有些字符串不能显示!

也就是说,假如你要表示一个在某个编码中没有对应的?Unicode?code?point,?通常会显示为一个???或者一个白色的小方框。

英文常用的一些编码有,?Windows-1252(Windows?9x?标准?for?西欧语言)

以及?ISO-8859-1,?aka?Latin-1(对任何西欧语言也有效)

如果用这些编码来尝试存储俄文字符,你会得到一堆的??

UTF?7,?8,?16?以及?32?都有一个优点,能够正确的存储任何的?code?point.

最简单,也是最重要的几个概念

====================================================================

一个字符串不指定它使用什么编码是没有意义的。

再也不要假定,?“纯”文本(plain?text)?是?ASCII.

没有?“纯文本”?这个东西。

如果你有一个字符串,在内存中,在文件中,或者在?Email?消息里,你必须知道它的编码是什么。否则你无法正确的解释或者显示给用户。

所有的诸如?“我的网页不能正常显示了”,或者?”Email?消息不能正常显示了“?之类的愚蠢问题,?都是因为,?没有告诉你到底是使用的那种编码,

UTF-8?还是?ASCII?还是?ISO?8859-1?或者?Windows?1252????那么自然无法正常的解释和显示,甚至不知道字符串该在哪里结束。

那么如何保留这样的编码标志,来表示字符串的编码??有一些基本的办法。

比如对于?Email?来说,在表单的?header?中加上:

Content-Type:text/plain;charset=”UTF-8″

对于?Web?页面来说,原来的做法是,?Web?服务器随着?web?页面本身一起,发送一个类似于?Content-Type?的?http?header.

(不是在?HTML?里面,而是作为一个?response?header?在?HTML?页之前发送)

这样做有一个问题。如果你的?Web?服务器同时有多个站点,站点由多个不同的人用不同的语言开发的程序混在一起。那么?Web?服务器将无从得知,

每一个文件是用什么编码方式写的。这样也就无法发送正确的?Content-Type?header.

如果你能够在每一个?HTML?文件中记录?Content-Type?信息,那么就很方便了。可这念头似乎也很疯狂,因为你还没有知道用什么编码方式去

读取这个文件,又怎么能读出编码信息呢?

幸好,几乎每一种编码中,对?32~127?的字符都解释的相同。所以你可以在每一个?html?文件中这么写:

<html>

<head>

<meta?http-equiv=”Content-Type”?content=”text/html;charset=utf-8″>

但是要注意,?这个?meta?标签必须放在?head?中靠前面的位置才能保证不会出问题。?因为?Web?服务器读到这里的时候,就会停止解析,

然后用读到的这个编码方式重新解析页面。

那么,作为?Web?浏览器来说,如果没有在?meta?标签中或者?http?headers?中发现?Content-Type,?会怎么样呢?

IE?是这么做的:

先尝试去猜,根据特定的字节出现在各种语言的典型的编码中的频率。

如果编码设定不正常,用户可以通过?View|Encoding?菜单来尝试不同的编码方式。(当然,不是每个人都知道该这样做)

在?VB,?COM,?Windows?NT/2000/XP?中,默认的字符串类型是?UCS-2(2字节)的。

在?C++?代码中,?我们可以定义字符串为?wchar_t(wide?char),同时用?wcs?系列的函数代替?str?系列的函数。

如?wcscat,?wcslen,?而不是?strcat,?strlen.

在?C?代码中,要创建?UCS-2?字符串的话,只要在前面加一个?”L”,?如?L”Hello”

对于?Web?页面,{zh0}统一为使用?UTF-8?编码。?这个编码已经被各种?web?浏览器支持了很多年了。

(完)

相关日志:

郑重声明:资讯 【Chong Tang's Blog » 每一个软件开发人员{jd1}必须掌握的关于Unicode 和 ...】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——