编码中惟一最重要的事实
即使你将我刚才所说的一切忘得一干二净,我也要请你记住一个极其重要的事实。有一个字符串,而不知道它所使用的编码方案是毫无意义的ASCII。。你再也不用将脑袋紧贴在沙地上而假想“纯”文本就是
纯文本不是这么一回事
如果在内存、文件或者电子邮件中有一个字符串,那么应该知道它使用的是什么编码方案,否则就不能将它正确地解释或者显示给用户。
其实,几乎所有诸如“Web网页好像是乱码”或者“在使用重音字母时无法阅读电子邮件”之类的愚蠢问题,都可以归结到天真的程序员身上,他不了解这样一个简单的事实,即如果不告诉我某个特定字符串是用UTF-8,或者ASCII,或者ISO 8859-1(Latin 1),或者Windows 1252(西欧)编码的,那么我就不能正确地显示,乃至弄清楚它在什么地方结束。编码方案数以百计,而在代码点127以上,拍脑袋已经没有效果了。
如何保存字符串用到的编码方案信息?喏,做这件事情有许多标准的方式。对于电子邮件信息,需要在表窗体标题放一个字符串
Content-Type: text/plain; charset="UTF-8"
对于Web页,最初的想法是Web服务器通过网页本身返回一个类似于Content-Type的http——不是在HTML当中而是作为一个响应标题在HTML页面之前返回。
这会引起一些问题。假设一个大型Web服务器拥有许多站点,成千上万的网页是由许多人使用多种不同语言发布的,网页使用任何在Microsoft FrontPage看来很适合产生的编码方案。由于Web服务器实际上不知道每个文件究竟是用什么编码方案写成的,因此它不能发送Content-Type标题。
如果能够使用某种特殊标记将HTML文件的Content-Type内容自然地放在HTML文件当中,会是很方便的。当然,这会让纯化论者发疯。不过,在知道HTML文件使用了什么编码方案之前,如何读取HTML文件呢?!幸运的是,几乎每个用得很普遍的编码方案,对码值32与127之间的字符都以相同方式进行处理。因此,人们总是可以充分展示HTML页而不必使用一些很稀奇的字母:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
不过,这里的meta标记确实位于<head>段中很靠前的位置。因为只要Web浏览器看到该标记,它就会停止解析页面,并在使用指定的编码方案重新解释整个页面之后进入下一轮。
如果Web浏览器在http报头与meta标记中都找不到任何Content-Type,该怎么办呢?Internet Explorer确实做了件非常有趣的事情:基于不同语言以典型编码方案得到的典型文本中各种字节出现的频率,试图猜出使用了什么语言与编码方案。由于各类不同的旧8字节代码页倾向于将其母语字母放在128与255之间的不同范围中,并且人类使用的每个语种具有不同特征的字母使用概率分布,因此使这种做法确实有了发挥作用的机会。是的,它确实显得有点怪异。不过,这种做法通常能够满足那些从不知道要使用Content-Type报头的幼稚网页编写人员,在Web浏览器中阅读网页的需要,并且看起来还很不错。
直到有一天,编写的网页确实与母语的字母使用概率分布情形不一致,Internet Explorer则认定它是朝鲜语而加以显示。在我看来,这表明了一点,Jon Postel的格言“宽进窄出”实在不是一条好的工程原则
[1]。不管怎么说,一旦用保加利亚语写的网站却以朝鲜语的形式出现(甚至是毫不相干的朝鲜文),可怜的读者该怎么办呢?使用菜单“View | Encoding(视图|编码)”试图应用一组不同的编码方案(东欧语种可用的编码方案至少有一打),直到图片能够较为清楚地显示出来为止。即使这位仁兄知道如此行事,可大多数人却不知道这一招。
对于由本人的公司Fog Creek Software发布的Web站点管理软件最新版本CityDesk
[2] 而言,我们决定在内部用UCS-2(2字节)Unicode处理一切。这种编码形式也是VisualBasic、COM与Windows NT/2000/XP作为其固有字符串类型使用的。在C++代码中,字符串就定义为wchar_t而不是char,并且使用以wcs打头的函数而不是以str打头的函数(比如说,使用wcscat与wcslen,而不是strcat与strlen)。要用C代码生成UCS-2文本字符串,就得像L"Hello"一样在字符串前面放一个L。
CityDesk发布Web页时,将网页转换为UTF-8编码形式,该编码方案多年来一直得到Web浏览器的良好支持。“
Joel on Software”(《Joel说软件》)的29种语言版本全部使用这种方式编码,我迄今为止还没有收到一个人的来信说他在浏览网页内容时遇到任何麻烦
[3]。
本章写得有点长,并且未能覆盖需要了解的一切字符编码与Unicode内容。但我还是希望你好好地读一读,它对于你回过头来看编程有一定好处,正如用抗生素,而不是猛药与符咒。这就是我现在留给你去做的一项任务。
[1] Jon Postel的话,引自信息科学学院的1981年9月发布的文档“RFC 791 – Internet Protocol”。
[2] 见www.fogcreek.com/CityDesk。
[3] www.joelonsoftware.com/navLinks/OtherLanguages.html。