翻译】每个开发者都必须了解的unicode和character sets(字符集)知识点

原文地址:https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/ 你有没有想过content-type这个Tag是做什么的?就是当你写HTML时应该写的但是你从来不知道应该填什么的content-type。 Content-Type: text/html; charset=utf-8 Content-Type: multipart/form-data; boundary=something 你有没有收到过一份邮件里面有一行“????????????“ 我惊讶地发现有太多的开发者并不了解character sets, encodings, Unicode这些东西。 几年前,一个测试问我FogBUGZ(译者注:可能是原作者开发的一个软件)能不能用日语处理收到的邮件。日语?他们有用日语写的邮件?我完全没想过这回事。当我们仔细检查用来解析MIME邮件的商用版ActiveX control时,我们发现它确实错误处理了字符集(character sets),所以我们不得不写了一份代码去回滚它的错误并且重新对邮件进行解析。当我去看另一个商用版库的源代码时,我发现它的字符代码实现也非常差劲。我联系了这个包的开发者,他说他“对这个包也做不了更多的事“。就像很多程序员一样,他只想这件事就这样过去得了。 但是这件事不会就这样过去!当我发现主流的web开发工具PHP竟然有一个完全忽视字符集编码的issue(complete ignorance of character encoding issues),blithely使用了8个bit处理字符(characters),导致他妈的几乎不可能拿php开发出好的互联网应用。我觉得,这一切都该结束了。 在此我宣誓:如果你是一个在2003年工作的程序员,而你不知道characters, character sets, encodings和Unicode的基础知识。假设你被我逮着了,那我就要惩罚你在潜艇里切6个月洋葱。我发誓我会这样做的。 还有一件事: 它没有那么难。 在本文我将告诉你每一个在职程序员都应该知道的基础知识。“普通文本等于ASCII等于字符(characters)都是8比特的”这种想法,不仅是错误的,而且是非常错误的。如果你还带着这种想法编程,那你差不多相当于一个不懂细菌的医生。求你在没读完这篇文章前不要再写任何一行代码了。 在开始之前,我有一个提醒,如果你是那种比较罕见的懂国际化(internationalization)的人,你会发现我接下来的讨论有点过于简单了。我只想把最简单的教给大家,这样每个人都知道发生什么事了,并且能编写可以处理各种语言的代码而不是只有英语(甚至不能带重读符号)。我还要提醒你,字符处理知识创建国际化软件的一小部分,我有时间可以写点其他方面的,但今天我们只讲字符集(character sets)。 历史角度 了解一件事情最简单的方式就是跟着时间了解它。 你可能认为我会讲一些上古时期的字符集(character sets)比如EBCDIC。但是我不会。EBCDIC跟你的生活一点关系也没有。我们不会讲这么远的事。 在中古时期,当Unix被发明出来而K&R正在写《The C Programming Language》的时候,一切都很简单。EBCDIC正在退出历史舞台了。唯一需要处理的字符是哪些不带重音的英文字母。我们有一套叫做ASCII的编码可以用32到127之间的数据代表每一个字符。空格是32,字母‘A’是65,诸如此类。这些数字可以很方便地用7个比特存储下来。那时候大部分电脑都是用8bit的字节(8-bit bytes), 所以你不仅可以存下每一个ASCII字符,你还多出来整整1个bit。如果你耍点小聪明,这1个比特可以用于自己的目的。WordStar中的那群傻瓜就把最高位用于表示每个单词的最后一个字母,导致WordStar只能打印英语。用小于32的数字表示的字符是无法打印的(unprintable),被用于诅咒别人。开个玩笑。它们被当作控制字符使用(control characters),比如7会让计算机蜂鸣,12会导致打印机上的当前纸张飞出,让新纸填入。 这一切都很美好,如果你是一个英语使用者的话。 因为每个字节使用了8个比特,许多人就想到,“我们可以把128-255的数字给自己用”。问题在于很多人同时有了相同的想法,而且他们对于128到255的数字的用法都有自己独特的想法。IBM-PC 有一些后来被称为 OEM 字符集的东西,它为欧洲语言提供了一些重音字符和一系列线条绘制字符… … 水平条、垂直条、右边有小铃铛悬挂的水平条等等。 你可以用这些线条字符在屏幕上画好看的盒子和直线,用这些字符集画的画你仍然能在你的干洗机上的8088电脑上看到。事实上,当人们开始在美国以外购买个人电脑时,各种不同的OEM字符集就被设想出来了,这些字符集都是为了自己的目的而使用前128个字符。比如130在一些电脑上会被显示为é,但是在以色列售卖的电脑会将130显示为希伯来语Gimel(译者注:原文的字符打不出来),所以当美国人发送résumés到以色列时,以色列人会收到r(Gimel)sum(Gimel)s。在很多情况下,比如在俄罗斯,关于如何使用前128个字符有非常多的想法,所以交换两个俄语文档都是可能有问题的。 这种OEM混战的情况最终被ANSI标准敲定下来。在ANSI标准中,大家对小于128的数字该做什么都取得了一致认知,这点很像ASCII,但是从128开始的数字根据你所在的地区不同就有很多不同的方式处理了。这些不同的处理系统叫做*code pages。*比如以色列的DOS用的code page叫做862, 而希腊人使用的是737。小于128都是一样的,但是从128开始都完全不同了,而很多有趣的字母就出现在128之上(包括128)。MS-DOS的国际化版本有许多code page, 这些code page能处理各种语言,从英语到冰岛语,它们甚至还有一些多语言(multilingual)的code page可以同时在一台电脑上处理世界语和加利西亚语。哇!但是,在同一台计算机上同时显示希伯来语和希腊语是完全不可能的,除非你编写自己的自定义程序,使用位图图形显示所有内容,因为希伯来语和希腊语需要不同的代码页,对大数有不同的解释。 与此同时,亚洲的有成千上万的文字(letter),这些文字用8个比特是不可能装的下的。有一种糟糕的系统叫做DBCS(double byte character set)用来解决这类问题。在DBCS中,一些文字用1个字节存储,而另一些则使用两个字节存储。在字符串中向前移动很容易,但向后移动却几乎不可能。我们鼓励程序员不要使用 s++ 和 s- 来前后移动字符串,而是调用 Windows 的 AnsiNext 和 AnsiPrev 等函数,它们知道如何处理整个混乱的字符串。...

August 1, 2023 · 2 min