代码改变世界

字符编码那些事儿

2010-12-06 16:57  MichaelYin  阅读(616)  评论(0编辑  收藏  举报

这几天专门花时间好好看了下字符编码的东西,在这里写篇随笔把知识好好梳理一下。

讲到字符编码,还是从最基本的ASCII讲起咯。

在计算机刚开始使用时候,人们必须用计算机里面的01这样的二进制组合来表示一些基本的英文字母和字符,当然了,每个人都可以有自己的一套编码标准来完成这个看似简单的功能,不过,为了不同的计算机系统之间能够相互通信而不产生混乱,那么所有的计算机就必须采用相同的编码标准,所以美国的标准化组织就出台了ASCII编码标准,统一规定了上述符号和字母用哪些二进制来表示。在这里要加的一点是,标准ASCII只用了7位,最前面的一位是用来做奇偶校验的。比如英文字母a在ASCII编码中用二进制01100001表示,十六进制就是61.

使用了ASCII编码后,原本的问题得到了很好的解决。但是,这里又有了新的问题,它并不能对其他非英语国家有很好的支持。比如,我如何编码汉字,日本文字,朝鲜文字,阿拉伯语,还有俄罗斯语等等。由于汉字这样的文字数目较多,用一个Byte(也就是8 bit)来提供编码方案显然是不现实的,这时,双字节字符集(DBCS:double-byte character set)就出现了,它的出现就是为了解决刚刚说到的这个问题的。

先来解释下什么是DBCS,DBCS简单的说就是使用两个Byte来表示一个字符,而且DBCS的最初的还是ASCII代码,所以说DBCS是兼容ASCII的。而且它表示ASCII只用了一个Byte,这里说一个问题,假设给了你一连串的Byte数组,你解码的时候如何知道这个Byte的解码需要用到他的下一个Byte呢?这里需要看前一个Byte的最高位,也就是最左边的那个bit,前面已经说过,ASCII只用了7位,如果最高位不用来做奇偶校验的话,那么最高位其实是0,DBCS中的前面的一个Byte的最高位如果为1的话,那么我就可以知道这个字符的解码需要用到后面一个Byte.

不同的国家都有一套自己国家的字符的DBCS的解决方案,比如中国字符集就是GB2312,日本是shift_jis,韩国是ks_c_5601-1987,由于出现了不同的字符集,所以这里又出现了另外一个问题,两个Byte在不同的字符集中代表的字符是不一样的,所以计算机在跨语言通信时很麻烦。

这里还需要另外提到的一点是,上面提到的DBCS其实都是对于一种叫做ANSI编码的扩展,也就是说其实上面提到的那几种DBCS都是ANSI编码,只不过在简体中文操作系统中,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。这样也就告诉我们,其实在简体中文windows的记事本里面,保存选项中的ANSI其实也就是GB2312 的意思。

上面说到了DBCS跨语言的困难之处,人们也希望能够解决这个问题。这时候Unicode编码方式的出现比较好的解决了这个问题。Unicode编码采用两个Byte来统一表示世界上所有的字符,也就是说,能被Unicode表示的字符二进制都是表示都是不一样的。这样带来的好处是显而易见的,统一采用Unicode编码,我们可以在一段文本中存储中文,日文,韩文。

Unicode的前128位仍然是ASCII码,只不过是在后面加了一个全部是0的Byte。比如a在ASCII码中是01100001,在Unicode中就是01100001 00000000。

实用中见到Unicode的频率其实没有UTF-8的频率高,下面来讲讲UTF-8.

还是回想刚刚那个问题,Unicode用两个Byte来表示ASCII中已经支持的字符,仅仅是在后面加了一个全是0的Byte,那些只需要用到ASCII字符的国家当然不爽了,因为使用Unicode编码在网络传输或者文本存储中意味着相比于ASCII编码我需要多出一倍的流量。刚刚才看的新闻,一个女士在俄罗斯用手机更新了下新浪微博,结果用了3000多的花费,这年头,流量值钱啊!!

因为Unicode仅仅是编码标准,而并没有指定传输过程中以什么方式进行存储和传输,换句话说,我们可以对其进行一番加工,只要最后查询字符的时候查的是Unicode的字符表就行了。

可以把UTF看成一个中间格式的字符集,它支持多国语言,同时对于ASCII中的字符做了优化,对于ASCII中的字符在传输和存储的时候只需要一个Byte,大大节省了磁盘空间和网络流量。不过对于中文字符,一般是需要三个Byte来传输和存储,反而增加了空间和流量。关于UTF-8和Unicode具体的转换这里就不做具体的阐述,有兴趣的朋友自己去找相关资料,我觉得一般的IT人士了解到这之间的联系就不错了。

下面来讲讲BOM,我们可以采用上述的多种编码方式来存储一段文字到txt文件中去,这是没有问题的,但是当我们需要读取这段文本的时候,麻烦就来了,因为我们不知道txt中的文本是采用的何种编码方式,如果没有采用正确的编码方式,乱码就会产生,BOM就是用来做这个事情的。我们将一段文字存储到txt中区,然后用十六进制编辑器打开,就可以看到这里面的细节。EF BB BF开头的txt文件表示文件采用了UTF-8,FEFF采用的是Big-Endian,如果没有BOM则系统会用默认的ANSI方式打开。

我尽量将字符编码之间的联系和区别讲清楚,注重原理而忽略了部分实现的具体的细节,有兴趣的朋友可以自己用16进制的编辑器自己去探索一下,如有疑问和错误,欢迎大家指出。。

最后,推荐几篇我觉得不错的文章编码,charset,乱码,unicode,utf-8与net简单释义(续)

编码,charset,乱码,unicode,utf-8与net简单释义