每个软件开发人员绝对,肯定必须绝对了解Unicode和字符集(无借口!)

2 篇文章 0 订阅
1 篇文章 0 订阅

每个软件开发人员绝对,肯定必须绝对了解Unicode和字符集



标题

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

每个软件开发人员绝对,肯定必须绝对了解Unicode和字符集(无借口!)

旨在分享,翻译的不是很好,见谅。


原文

原文网址:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

译文

每个软件开发人员绝对,肯定必须绝对了解Unicode和字符集(无借口!)

​ -2003年10月8日,作者:JOEL SPOLSKY

​ 曾经对这个神秘的Content-Type标签感到好奇吗?您知道,您应该将其放入HTML中,但您从不完全知道它应该是什么?

​ 您是否收到过来自保加利亚朋友的电子邮件,主题行为“ ???? ??? ??? ???”?

​ 我很沮丧地发现,究竟有多少软件开发人员并没有真正完全赶上神秘的字符集,编码,Unicode之类的世界。几年前,FogBUGZ的Beta测试人员想知道它是否可以处理日语传入的电子邮件。日本?他们有日文电子邮件吗?我不知道。当我仔细查看用于解析MIME电子邮件的商业ActiveX控件时,我们发现它对字符集的处理完全不正确,因此我们实际上不得不编写英勇的代码来撤消所做的错误转换并正确地重做。当我查看另一个商业图书馆时,它的字符代码实现也完全被破坏了。我与那个程序包的开发人员通讯,他认为他们“对此无能为力”。像许多程序员一样,他只是希望一切都会以某种方式结束。

​ 但是不会。当我发现流行的Web开发工具PHP几乎完全不了解字符编码问题时,仅仅使用8位字符,几乎无法开发出良好的国际Web应用程序,我认为这就足够说明问题了。

​ 因此,我有个公告:如果您是一位在2003年工作的程序员,并且您不了解字符,字符集,编码和Unicode的基础知识,而我抓住了您,我将通过让您受到惩罚在潜水艇中将洋葱皮剥皮6个月。我发誓。

​ 还有一件事情:

这并不难。

​ 在这篇文章中,我将告诉你每个程序员都应该知道的事情。所有那些关于“纯文本= ascii =字符为8位”的东西不仅是错误的,而且是无可救药的错误,如果您仍然以这种方式进行编程,那么您不比一个不相信细菌的医生强多少。
在阅读完本文之前,请不要再写代码。

​ 在开始之前,我应该提醒您,如果您是了解国际化的少数人之一,那么您会发现我的整个讨论有点过于简单。我只是想在这里设置一个最小的标准,这样每个人都能理解正在发生的事情,并能够编写代码,希望能够处理除英语子集之外的任何语言的文本,而英语子集不包括带有口音的单词。我要提醒你的是,字符处理只是软件国际化的一小部分,但我一次只能写一件事,所以今天是字符集。

回顾历史

​ 理解这些东西最简单的方法就是按时间顺序来。

​ 您可能认为我将在这里讨论非常古老的字符集,比如EBCDIC。好吧,我不会的。EBCDIC与你的生活无关。我们不需要回到那么遥远的过去。

​ 早在半个古老的时代,当发明Unix并K&R编写C语言时,一切都非常简单。 EBCDIC即将退出。唯一重要的字符是无重音的英文字母,我们为他们提供了一个名为ASCII的代码,该代码能够使用32到127之间的数字表示每个字符。空格为32,字母“ A”为65,依此类推。这可以方便地以7位存储。那时的大多数计算机都使用8位字节,因此,不仅可以存储所有可能的ASCII字符,而且还有很多余地,如果您感到不便,可以将剩余的用于自己的用途: WordStar的灯泡实际上打开了高位以指示单词中的最后一个字母,仅将WordStar谴责为英文文本。低于32的代码被称为不可打印的,并被用于惩罚。开玩笑。它们用于控制字符,例如7会使计算机发出哔哔声,而12则导致当前纸张从打印机中飞出,并送入新的纸张。

​ 假设您会说英语,那么一切都很好。

​ 因为字节最多可以容纳8位,所以很多人开始想:“天哪,我们可以出于自己的目的使用代码128-255。”麻烦的是,很多人同时有这个想法,并且他们对从128到255之间的空间应该如何使用有自己的想法。IBM-PC具有后来被称为OEM字符集的东西,该字符集为欧洲语言提供了一些带重音的字符以及一系列的线条画字符。水平条,垂直条,右侧悬垂有少量斜角的水平条等,您可以使用这些线条绘制字符在屏幕上制作漂亮的框和线条,您仍然可以在8088计算机上看到它们的运行情况。实际上,人们开始在美国以外的地方购买PC时,就构想了各种不同的OEM字符集,它们都将后128个字符用于自己的目的。例如,在某些PC上,字符代码130将显示为é,但在以色列出售的计算机上,该字符是希伯来字母Gimel(ג),因此,当美国人将简历发送给以色列时,它们将以rגsumג的形式到达。在很多情况下,例如俄语,对于使用大写128字符的处理方法有很多不同的想法,因此您甚至无法可靠地交换俄语文档。

​ 最终,这个免费的OEM成为了ANSI标准。在ANSI标准中,每个人都同意在128以下进行处理,这与ASCII几乎相同,但是根据居住地的不同,有很多不同的方法可以处理128以上的字符。这些不同的系统称为代码页。例如,在以色列,DOS使用的代码页称为862,而希腊用户使用的是737。128以下的代码相同,但与所有有趣的字母所在的128以上的代码不同。全国版本的MS-DOS有许多这样的代码页,处理从英语到冰岛语的所有内容,甚至有几个“多语言”代码页可以在同一台计算机上执行世界语和加利西亚语!哇!但是,例如,除非您编写自己的自定义程序以使用位图图形显示所有内容,否则在同一台计算机上使用希伯来语和希腊语是完全不可能的,因为希伯来语和希腊语需要不同的代码页,且对高数字的解释不同。

​ 同时,在亚洲,甚至更疯狂的事情正在考虑到亚洲字母有成千上万个字母的事实,而这些字母永远都不能被8位容纳。这通常是通过称为DBCS的凌乱系统解决的,该系统是“双字节字符集”,其中一些字母存储在一个字节中,而另一些字母存储在两个字节中。连续前进很容易,但是晃荡几乎不可能后退。鼓励程序员不要使用s ++和s–来回移动,而是调用诸如Windows的AnsiNext和AnsiPrev之类的函数,它们知道如何处理整个混乱。

​ 但是,大多数人还是假装一个字节是一个字符,一个字符是8位,并且只要您从未将字符串从一台计算机移动到另一台计算机,或者说一种以上的语言,它总是可以正常工作的。但是,当然,一旦Internet出现,将字符串从一台计算机移到另一台计算机就变得很普遍了,整个混乱局面逐渐恶化。幸运的是,已经发明了Unicode。

Unicode

​ Unicode是一种勇敢的尝试,它创建了一个单一的字符集,包括地球上所有合理的书写系统和一些虚构的系统,比如Klingon。有些人错误地认为Unicode就是一个16位的代码,每个字符占用16位,因此有65,536个可能的字符。
实际上,这是不对的。这是关于Unicode最常见的误解,所以如果您这样认为,也没什么不好的。

​ 事实上,Unicode对字符有不同的理解方式,你必须理解Unicode对事物的理解方式,否则就没有意义。

​ 到目前为止,我们假设一个字母映射到一些位,你可以存储在磁盘或内存:

​ A -> 0100 0001

​ 在Unicode中,一个字母映射到一个叫做代码点的东西,这仍然只是一个理论概念。如何在内存或磁盘中表示代码点是另一回事。

​ 在Unicode中,字母A是柏拉图式的理想。它只是漂浮在天堂:

A

​ 该柏拉图式A与B不同,并且与a不同,但与A和A和A相同。这种想法是,Times New Roman字体中的A与Helvetica字体中的A相同,但与“ a “小写”似乎并没有引起很大争议,但是在某些语言中,仅弄清楚什么是字母会引起争议。德语字母ß是真实字母还是只是写ss的一种奇特方式?如果字母的形状在单词的末尾发生变化,那是另一个字母吗?希伯来语说是,阿拉伯语说不。无论如何,在过去的十年左右的时间里,Unicode协会的聪明人一直在解决这个问题,伴随着大量的高度政治辩论,您不必担心。他们已经弄清楚了。

​ 每个字母表中的每个柏拉图式的字母都被统一码联盟分配了一个神奇的数字,它是这样写的:U+0639。这个神奇的数字称为代码点。U+表示“Unicode”,数字是十六进制的。U+0639是阿拉伯字母Ain。英文字母A应该是U+0041 您可以使用Windows 2000/XP上的charmap实用程序或访问Unicode网站找到它们。

​ Unicode所能定义的字母数量并没有真正的限制,实际上它们已经超过了65,536个,所以并不是每个Unicode字母都可以压缩成两个字节,但无论如何这是一个神话。

​ 假设我们有一个字符串:

Hello

​ 在Unicode中对应于这五个编码点:

​ U+0048 U+0065 U+006C U+006C U+006F.

​ 只是一堆代码点。数字,真的。我们还没有说过如何将它存储在内存中或在电子邮件中表示它。

Encodings

​ 这就是编码的用武之地。

​ Unicode编码的最早的想法,导致了关于这两个字节的神话,就是,嘿,让我们把这些数字存储在每个字节中。

所以 hello 变成了

​ 00 48 00 65 00 6C 00 6C 00 6F

​ 对吧?不要这么快!难道不能是:

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

好吧,从技术上讲,是的,我确实相信这可以,而且事实上,早期的实现者希望能够以高端或低端模式存储他们的Unicode代码点,无论哪种特定的CPU最快,而且,从早晨到傍晚,已经有两种存储Unicode的方法。因此人们被迫提出了一个奇怪的约定,即在每个Unicode字符串的开头存储一个FE FF。这被称为Unicode字节顺序标记,如果您要交换高字节和低字节,它将看起来像FF FE,并且读取字符串的人将知道他们必须交换其他所有字节。哦,并不是所有Unicode字符串的开头都有字节顺序标记。

一段时间以来,这似乎已经足够了,但是程序员们一直在抱怨。他们说:“看看所有这些零!”,因为他们是美国人,而且他们正在看的英文文本很少使用U+00FF以上的代码点。还有来自加利福尼亚的自由派嬉皮士,他们想保留(冷笑)。如果他们是德州人,他们不会介意将字节数增加一倍。但是那些加利福尼亚的懦弱的人不愿意将字符串存储空间增加一倍,而且无论如何,已经有很多该死的文件在使用各种各样的ANSI和DBCS字符集,谁来转换它们呢?,谁来转换它们呢?摩伊人吗?仅出于这个原因,大多数人几年来一直决定忽略Unicode,与此同时情况变得更糟了。

因此发明了UTF-8的出色概念。 UTF-8是另一个使用8位字节将Unicode代码点的字符串(那些神奇的U+数字)存储在内存中的系统。在UTF-8中,从0-127的每个代码点都存储在一个字节中。实际上,只有代码点128和更高的代码点才使用2、3(最多6个字节)存储。

​ 这样做的好处是,英语文本在UTF-8中的外观与在ASCII中的外观完全相同,因此美国人甚至不会注意到任何错误。只有世界其他地方才可以跳过障碍。具体来说,Hello,即U+0048 U+0065 U+006C U+006C U+006F,将被存储为48 65 6C 6C 6F,这可是!与存储在ASCII和ANSI中以及地球上每个OEM字符集相同。现在,如果您大胆使用重音字母,希腊字母或克林贡字母,则必须使用多个字节来存储单个代码点,但是美国人永远不会注意到。 (UTF-8还有一个很好的特性,即希望使用单个0字节作为空终止符的老式字符串处理代码不会截断字符串)。

​ 到目前为止,我已经介绍了三种编码Unicode的方法。传统的双字节存储方法称为UCS-2(因为它有两个字节)或UTF-16(因为它有16位),您仍然需要确定它是高端UCS-2还是低端UCS-2。还有一个流行的新UTF-8标准,它有一个很好的特性,那就是它的工作性能也很好,如果你有一个很好的巧合,那就是英语文本和一些愚蠢的程序完全没有意识到除了ASCII之外还有别的东西。

​ 实际上,还有许多其他编码Unicode的方法。有一种叫做UTF-7的东西,它与UTF-8很像,但是可以保证高位始终为零,因此,如果您必须通过某种认为7位相当严格的警察状态电子邮件系统来传递Unicode,足够了,谢谢,它仍然可以毫发无损地挤过。有UCS-4,它以4个字节存储每个代码点,它具有很好的属性,即每个代码点可以存储在相同数量的字节中,但是,太可惜了,即使是德州人也不会那么大胆以至于浪费那么多的内存。

​ 实际上,现在您已经考虑了以Unicode码点表示的柏拉图式理想字母的方式,那些unicode码点也可以采用任何老式的编码方案进行编码!例如,您可以使用ASCII编码Hello(U+0048 U+0065 U+006C U+006C U+006F) 的Unicode字符串,或者使用旧的OEM希腊编码,希伯来ANSI编码或数百种编码中的任何一种到目前为止已经发明的,只有一个问题:有些字母可能不会出现!如果您尝试用Unicode表示的编码中没有等效的代码,通常会出现一个问号:?或者,如果您真的很好,那就放一个盒子。你得到了什么? ->�

​ 有数百种传统编码,它们只能正确存储一些代码点,而将所有其他代码点更改为问号。一些流行的英语文本编码是Windows-1252(西欧语言的Windows 9x标准)和ISO-8859-1,又名Latin-1(对任何西欧语言也有用)。但是,尝试以这些编码存储俄语或希伯来语字母,则会出现很多问号。 UTF 7、8、16和32都具有能够正确存储任何代码点的优点。

关于编码的最重要的事实

​ 如果您完全忘记了我刚才解释的所有内容,请记住一个极其重要的事实。不知道字符串使用什么编码就没有意义。您不能再把头埋在沙子里,并假装“纯文本”为ASCII。

没有纯文本这样的东西。

​ 如果您在内存,文件或电子邮件中有字符串,则必须知道字符串的编码,否则无法解释它或将其正确显示给用户。

​ 几乎每个愚蠢的“我的网站看起来像胡言乱语”或“当我使用重音符号时她都无法阅读我的电子邮件”的问题归结为一个天真的程序员,他不理解一个简单的事实,即如果您不告诉我某个特定的字符串使用UTF-8或ASCII或ISO 8859-1(拉丁语1)或Windows 1252(西欧语)编码,您根本无法正确显示它,甚至无法弄清楚它的结尾。有上百种编码,并且在代码点127之上,所有投注均无效。

​ 我们如何保存有关字符串使用哪种编码的信息?好吧,有标准的方法可以做到这一点。对于电子邮件,应该在表单的标题中包含一个字符串

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

​ 对于网页,最初的想法是Web服务器将与网页本身一起返回类似的Content-Type http标头-不在HTML本身中,而是作为在HTML页之前发送的响应标头之一。

​ 这会引起问题。假设您有一台大型Web服务器,其中包含许多站点,数百页的页面由许多人以多种不同的语言提供,并且全部使用Microsoft FrontPage认为合适的编码方式生成。网络服务器本身并不真正知道每个文件的编码方式,因此它无法发送Content-Type标头。

​ 如果可以使用某种特殊标记将HTML文件的Content-Type放到HTML文件本身中,将会很方便。当然,这使纯粹主义者发疯了……您如何才能读取HTML文件,直到您知道它的编码是什么?!幸运的是,几乎所有常用的编码都使用32到127之间的字符来做同样的事情,因此您始终可以在HTML页面上达到这一目的而无需开始使用有趣的字母:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

​ 但是,该meta标记确实必须是部分中的第一件事,因为一旦网络浏览器看到此标记,它将立即停止解析页面,并在使用您指定的编码重新解释整个页面后重新开始。

​ 如果网络浏览器在http标头或meta标记中找不到任何Content-Type,该怎么办? Internet Explorer实际上做了一些非常有趣的事情:它尝试根据各种语言以各种语言的典型编码出现在典型文本中的各个字节的出现频率来猜测。因为各种旧的8位代码页都倾向于将其本国字母放在128到255之间的不同范围内,并且由于每种人类语言都有不同的字母用法特征直方图,因此实际上有工作的机会。确实很奇怪,但是它的确似乎经常工作,以至于那些从未知道需要Content-Type标头的天真的网页编写者会在网络浏览器中查看他们的页面,看起来还可以,直到有一天,他们写的东西并不完全符合其母语的字母频率分布,Internet Explorer决定使用朝鲜语并以此显示,这证明了波斯特尔定律关于“在所散发的内容中保持保守并在您的内容中放宽态度”的观点坦率地说,“接受”不是一个好的工程原理。无论如何,这个用保加利亚语写成但似乎是朝鲜人(甚至没有凝聚力的朝鲜语)的可怜网站读者怎么办?他使用View |编码菜单,然后尝试一堆不同的编码(东欧语言至少有十二种编码),直到图片更清晰为止。如果他知道这样做,大多数人都不知道。

​ 对于我公司发布的网站管理软件CityDesk的最新版本,我们决定在内部使用UCS-2(两个字节)Unicode进行所有操作,这是Visual Basic,COM和Windows NT / 2000 / XP所使用的他们的本机字符串类型。在C ++代码中,我们只将字符串声明为wchar_t(“宽字符”)而不是char,并使用wcs函数代替str函数(例如,使用wcscat和wcslen代替strcat和strlen)。要在C代码中创建文字UCS-2字符串,您只需在其前面加上L即可:L“ Hello”。

​ CityDesk发布网页时,会将其转换为UTF-8编码,多年来,Web浏览器一直很好地支持该编码。这就是Joel on Software的所有29种语言版本的编码方式,而且我还没有听到一个人在查看它们时遇到任何麻烦。

​ 本文得到相当长,我不可能涵盖一切知道字符编码和Unicode,但我希望如果你读过这么远,你知道足够的回到编程,使用抗生素,而不是水蛭和法术,一个任务,我将离开你了。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值