为什么#提问奥巴马的微博在屏幕上是乱码:了解你的UTF-8,Unicode,ASCⅡ和ANSI解码总统先生

[原文发表地址] Why the #AskObama Tweet was Garbled on Screen: Know your UTF-8, Unicode, ASCII and ANSI Decoding Mr. President

[原文发表时间] 2011-07-07 01:37

更新:在黑客新闻上评论软件的承办商/供应商所包含的更多技术信息。他们是个很棒的公司,很好地处理了这样的小问题,维护了其信誉。我的意思是,我们应该这样来看,这是件很有趣,并且只有我们程序员懂是很诡异的一件事,但我们最终都认可的是,奥巴马应该立刻取缔智能引号的使用。

众议院发言人John Boehner几天前发表了这个微博。注意,这不是一篇政治性博文。

After embarking on a record spending binge that’s left us deeper in debt, where are the jobs? #AskObama

在#提问奥巴马微博中,这条微博显示在大屏幕上,结果出现了混乱状况,显示为:

After embarking on a record spending binge that’s left us deeper in debt, where are the jobs? #AskObama

上百万的程序员,不顾政治党派,齐声唏嘘。首先,有人搞砸了他们UTF-8的解码,其次,我们的总统在阅读过程中还没有发现这个编码bug!好吧,也许第二个原因只是我个人看法,但仍然只能用悲剧来形容。之后,总统先生取笑一番发言人的输入功底,而所有的报纸和新闻组织都围绕这个“乱码微博”喋喋不休。

Boehner应该打的是“that's”,但他却打了“that’s”。你需要注意的是那个“智能”单引号。他用Tweetdeck发布这条微博,而且可能是在Mac上发布的。也有可能他在微软Word里打完了再复制黏贴的,因为Word最喜欢把引号和单引号转成像’这样的智能引号和智能单引号了。

我可以获取John Boehner的用户ID(不是他的微博名称,而是代表John的数字账号),只需通过这个在线工具https://www.idfromuser.com.就可以获取了。我看见他的账号是5357812,这样我就可以像获取RSS/XML一样获取他的时间表了,就像这样:https://twitter.com/statuses/user_timeline/5357812.rss 或者像获取JSON这样:https://twitter.com/statuses/user_timeline/5357812.json

当我获取这个时间表,HTTP Headers显示它的编码是“UTF-8”,看到了吗?

Content-Type: application/json; charset=utf-8

五年前,我曾写过关于“UTF-8的重要性”的博文。如果你观察下JSON,找到ID是88618213008621568的微博,你可以看到JSON中编码的原始文本:

"text":"After embarking on a record spending binge that\u2019s left us deeper in debt, where are the jobs?"

看到当中的\u2019了吗?在Windows里(即使你不是编程人员,你也会有这个程序),找到开始菜单,运行“Charmap”。找一下,你会看到U+2019就是右单引号。注意它在所有字符列表最下面。它不是一般的像A到Z或者a到z这样的基础字符,而是那些看上去不错但会造成麻烦的特殊字符之一。

1

如果我在记事本里建一个文本文件,看上去和这个差不多,并命名为text.txt,比方说,使用Save As,并确保使用UTF-8来编码。

After embarking on a record spending binge that’s left us deeper in debt, where are the jobs?

然后把它加载到免费得HEX编辑器中(或者在线版)我得到这个:

2

注意那部分有’的,其实占了三个完整字节!E2 80 99。

UTF-8是一种编码,目的不仅仅是支持亿万不同的字符,还要与ASCII(美国信息交换标准代码)兼容。若非如此,我们不可能看到这条微博里的大部分字符。在这个案例中,只有’有点混乱而已。

代码点是U+2019,也就是Programmer模式下Windows计算器中的0010 0000 0001 1001。亲爱的读者,你也有哦。这里是一些拓展的各种编码信息,你可以在维基百科中阅读。

U+2019的值延展到了0010 0000 0001 1001,正如我说的,根据下列规则进行延展:

zzzzyyyy yyxxxxxx ->

1110zzzz

10yyyyyy

10xxxxxx

也就让我们得到了下列结果:

11100010 -> E2

10000000 -> 80

10011001 -> 99

因此,“that’s”的编码就是

74 68 61 74 E2 80 99 73

我加粗了’。然后,重新看一遍,这次使用Extended ASCII(ANSI Windows 1252 Code page)我们得到’的扩展:

that’s

觉得我扯太远了?为什么我不简单地说“这个软件读取的是用UTF-8编码的JSON微博流,然后用ANSI Windows code page 1252显示了出来。”?因为这样就会很无趣,一点都不好玩。

不管怎样,为白宫负责这部分内容的公司的确是搞糟了,他们应该事先测试一下的。这是个典型的懒散的程序员才会犯的错误,而在这种场合暴露出来,我感到非常的失望。我希望他们(供应商)会因此觉得愧疚。那个公司好像是叫“Mass Relevance”,这里提供一些关于Mass Relavance的最新文章,当然也有他们针对此次问题的解决。

测试,测试,测试,我的朋友们。而且不仅仅要测试,还要深刻理解。这不是课堂里可以学到的,要不是在全国电视上,在总统面前看到这个错误,谁也不会当回事。

更新:供应商在评论中说了以下这些话,说得还是不错的。

“这的确是我们的过错。问题不在我们数据反馈的编码上,而在于HTML文件通过ISO-8859-1发出去的。第二,我们将微博文本插入到DOM中,浏览器自动把UTF-8字符译成了ISO-8859-1。我们的可视化建立在其他平台上,因此服务器并未正确配置将文本/html以UTF-8传送,尽管HTML文件是这样被编码的。这是这次完美的活动中唯一的问题(尽管很明显)。在此,我要向奥巴马总统,发言人Boehner以及Jack Dorsey对这个错误表达我诚挚的歉意。如果看了这篇博文的读者们觉得这个问题很愚蠢,也请多包涵,想象下我们的感受吧。开发环境不等于生产环境。如果我们可以在一开始就在HTML中加入 <meta charset="utf-8"> ,那也就不会发生这种状况了。

还有重要的一点就是,请不要妄加推断其他的平台(尤其是涉及到编码的),记得多加charset meta tag

3

文本编码对任意年龄段的人都是很有趣的。希望你们喜欢!

*喜欢这篇博文?那就把我推向电视吧。真正的技术记者Pogue会对这种东西感兴趣的,他会想和大家一起分享的!ABC新闻?我很有空,我还有Skype,欢迎联络我!