`
石头的日记
  • 浏览: 199094 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类

关于编码的问题

阅读更多

在做程序的时候难免会遇到些编码问题,如果对字符码有一些了解,相信解决起来时就会得心应手些了

 

从ASCII码说起

 

说到字符编码,不得不说ASCII码的简史。计算机一开始发明的时候是用来解决数字计算的问题,后来人们发现,计算机还可以做更多的事,例如文本处 理。但由于计算机只识“数”,因此人们必须告诉计算机哪个数字来代表哪个特定字符,例如65代表字母‘A’,66代表字母‘B’,以此类推。但是计算机之间字符-数字的对应关系必须得一致,否则就会造成同一段数字在不同计算机上显示出来的字符不一样 。因此美国国家标准协会ANSI制定了一个标准,规定了常用字符的集合以及每个字符对应的编号,这就是ASCII字符集(Character Set),也称ASCII码。

当时的计算机普遍使用8比特字节作为最小的存储和处理单元,加之当时用到的字符也很少,26个大小写英文字母还有数字再加上其他常用符号,也不到100个,因此使用7个比特位就可以高效的存储和处理ASCII码,剩下最高位1比特被用作一些通讯系统的奇偶校验。

注意,字节代表系统能够处理的最小单位,不一定是8比特。只是现代计算机的事实标准就是用8比特来代表一个字节。在很多技术规格文献中,为了避免产 生歧义,更倾向于使用8位组(Octet)而不是字节(Byte)这个术语来强调8个比特的二进制流。下文中为了便于理解,我会延用大家熟悉的“字节”这 个概念。

ASCII table

ASCII字符集由95个可打印字符(0x20-0x7E)和33个控制字符(0x00-0x19,0x7F)组成。可打印字符用于显示在输出设备 上,例如荧屏或者打印纸上,控制字符用于向计算机发出一些特殊指令,例如0x07会让计算机发出哔的一声,0x00通常用于指示字符串的结束,0x0D和 0x0A用于指示打印机的打印针头退到行首(回车)并移到下一行(换行)。

那时候的字符编解码系统非常简单,就是简单的查表过程。例如将字符序列编码为二进制流写入存储设备,只需要在ASCII字符集中依次找到字符对应的字节,然后直接将该字节写入存储设备即可。解码二进制流的过程也是类似。

OEM字符集的衍生

当计算机开始发展起来的时候,人们逐渐发现,ASCII字符集里那可怜的128个字符已经不能再满足他们的需求了。人们就在想,一个字节能够表示的 数字(编号)有256个,而ASCII字符只用到了0x00~0x7F,也就是占用了前128个,后面128个数字不用白不用,因此很多人打起了后面这 128个数字的主意。可是问题在于,很多人同时有这样的想法,但是大家对于0x80-0xFF这后面的128个数字分别对应什么样的字符,却有各自的想 法。这就导致了当时销往世界各地的机器上出现了大量各式各样的OEM字符集。

下面这张表是IBM-PC机推出的其中一个OEM字符集,字符集的前128个字符和ASCII字符集的基本一致(为什么说基本一致呢,是因为前32 个控制字符在某些情况下会被IBM-PC机当作可打印字符解释),后面128个字符空间加入了一些欧洲国家用到的重音字符,以及一些用于画线条画的字符。

IBM-PC OEM字符集

事实上,大部分OEM字符集是兼容ASCII字符集的,也就是说,大家对于0x00~0x7F这个范围的解释基本是相同的,而对于后半部分0x80~0xFF的解释却不一定相同。甚至有时候同样的字符在不同OEM字符集中对应的字节也是不同的。

不同的OEM字符集导致人们无法跨机器交流各种文档。例如职员甲发了一封简历résumés给职员乙,结果职员乙看到的却是rג sumג s,因为é字符在职员甲机器上的OEM字符集中对应的字节是0x82,而在职员乙的机器上,由于使用的OEM字符集不同,对0x82字节解码后得到的字符却是ג

多字节字符集(MBCS)和中文字符集

上面我们提到的字符集都是基于单字节编码,也就是说,一个字节翻译成一个字符。这对于拉丁语系国家来说可能没有什么问题,因为他们通过扩展第8个比 特,就可以得到256个字符了,足够用了。但是对于亚洲国家来说,256个字符是远远不够用的。因此这些国家的人为了用上电脑,又要保持和ASCII字符 集的兼容,就发明了多字节编码方式,相应的字符集就称为多字节字符集。例如中国使用的就是双字节字符集编码(DBCS,Double Byte Character Set)。

对于单字节字符集来说,代码页中只需要有一张码表即可,上面记录着256个数字代表的字符。程序只需要做简单的查表操作就可以完成编解码的过程。

代码页是字符集编码的具体实现,你可以把他理解为一张“字符-字节”映射表,通过查表实现“字符-字节”的翻译。下面会有更详细的描述。

而对于多字节字符集,代码页中通常会有很多码表。那么程序怎么知道该使用哪张码表去解码二进制流呢?答案是,根据第一个字节来选择不同的码表进行解析

例如目前最常用的中文字符集GB2312,涵盖了所有简体字符以及一部分其他字符;GBK(K代表扩展的意思)则在GB2312的基础上加入了对繁 体字符等其他非简体字符(GB18030字符集不是双字节字符集,我们在讲Unicode的时候会提到)。这两个字符集的字符都是使用1-2个字节来表 示。Windows系统采用936代码页来实现对GBK字符集的编解码。在解析字节流的时候,如果遇到字节的最高位是0的话,那么就使用936代码页中的 第1张码表进行解码,这就和单字节字符集的编解码方式一致了。

image

当字节的高位是1的时候,确切的说,当第一个字节位于0x81 –0xFE之间时,根据第一个字节不同找到代码页中的相应的码表,例如当第一个字节是0x81,那么对应936中的下面这张码表:

image

(关于936代码页中完整的码表信息,参见MSDN:http://msdn.microsoft.com/en-us/library/cc194913%28v=MSDN.10%29.aspx .)

按照936代码页的码表,当程序遇到连续字节流0x81 0x40的时候,就会解码为“丂”字符。

ANSI标准、国家标准、ISO标准

不同ASCII衍生字符集的出现,让文档交流变得非常困难,因此各种组织都陆续进行了标准化流程。例如美国ANSI组织制定了ANSI标准字符编码(注意,我们现在通常说到ANSI编码,通常指的是平台的默认编码,例如英文操作系统中是ISO-8859-1,中文系统是GBK ),ISO组织制定的各种ISO标准字符编码,还有各国也会制定一些国家标准字符集,例如中国的GBK,GB2312和GB18030。

操作系统在发布的时候,通常会往机器里预装这些标准的字符集还有平台专用的字符集,这样只要你的文档是使用标准字符集编写的,通用性就比较高了。例 如你用GB2312字符集编写的文档,在中国大陆内的任何机器上都能正确显示。同时,我们也可以在一台机器上阅读多个国家不同语言的文档了,前提是本机必 须安装该文档使用的字符集。

Unicode的出现

虽然通过使用不同字符集,我们可以在一台机器上查阅不同语言的文档,但是我们仍然无法解决一个问题:在一份文档中显示所有字符 。为了解决这个问题,我们需要一个全人类达成共识的巨大的字符集,这就是Unicode字符集。

Unicode字符集概述

Unicode字符集涵盖了目前人类使用的所有字符,并为每个字符进行统一编号,分配唯一的字符码(Code Point)。Unicode字符集将所有字符按照使用上的频繁度划分为17个层面(Plane),每个层面上有216 =65536个字符码空间。

image

其中第0个层面BMP,基本涵盖了当今世界用到的所有字符。其他的层面要么是用来表示一些远古时期的文字,要么是留作扩展。我们平常用到的Unicode字符,一般都是位于BMP层面上的。目前Unicode字符集中尚有大量字符空间未使用。

编码系统的变化

在Unicode出现之前,所有的字符集都是和具体编码方案绑定在一起的,都是直接将字符和最终字节流绑定死了,例如ASCII编码系统规定使用7 比特来编码ASCII字符集;GB2312以及GBK字符集,限定了使用最多2个字节来编码所有字符,并且规定了字节序。这样的编码系统通常用简单的查 表,也就是通过代码页就可以直接将字符映射为存储设备上的字节流了。例如下面这个例子:

image

这种方式的缺点在于,字符和字节流之间耦合得太紧密了,从而限定了字符集的扩展能力。假设以后火星人入住地球了,要往现有字符集中加入火星文就变得很难甚至不可能了,而且很容易破坏现有的编码规则。

因此Unicode在设计上考虑到了这一点,将字符集和字符编码方案分离开。

字符编码系统

也就是说,虽然每个字符在Unicode字符集中都能找到唯一确定的编号(字符码,又称Unicode码),但是决定最终字节流的却是具体的字符编码 。例如同样是对Unicode字符“A”进行编码,UTF-8字符编码得到的字节流是0x41,而UTF-16(大端模式)得到的是0x00 0x41。

常见的Unicode编码

UCS-2/UTF-16

如果要我们来实现Unicode字符集中BMP字符的编码方案,我们会怎么实现?由于BMP层面上有216 =65536个字符码,因此我们只需要两个字节就可以完全表示这所有的字符了。

举个例子,“中”的Unicode字符码是0x4E2D(01001110 00101101),那么我们可以编码为01001110 00101101(大端)或者00101101 01001110 (小端)。

UCS-2和UTF-16对于BMP层面的字符均是使用2个字节来表示,并且编码得到的结果完全一致。不同之处在于,UCS- 2最初设计的时候只考虑到BMP字符,因此使用固定2个字节长度,也就是说,他无法表示Unicode其他层面上的字符,而UTF-16为了解除这个限 制,支持Unicode全字符集的编解码,采用了变长编码,最少使用2个字节,如果要编码BMP以外的字符,则需要4个字节结对 ,这里就不讨论那么远,有兴趣可以参考维基百科:UTF-16/UCS-2

Windows从NT时代开始就采用了UTF-16编码,很多流行的编程平台,例如.Net,Java,Qt还有Mac下的Cocoa等都是使用UTF-16作为基础的字符编码。例如代码中的字符串,在内存中相应的字节流就是用UTF-16编码过的。

UTF-8

UTF-8应该是目前应用最广泛的一种Unicode编码方案。由于UCS-2/UTF-16对于ASCII字符使用两个字节进行编码,存储和处理 效率相对低下,并且由于ASCII字符经过UTF-16编码后得到的两个字节,高字节始终是0x00,很多C语言的函数都将此字节视为字符串末尾从而导致 无法正确解析文本。因此一开始推出的时候遭到很多西方国家的抵触,大大影响了Unicode的推行。后来聪明的人们发明了UTF-8编码,解决了这个问 题。

UTF-8编码方案采用1-4个字节来编码字符,方法其实也非常简单。

image

(上图中的x代表Unicode码的低8位,y代表高8位)

对于ASCII字符的编码使用单字节,和ASCII 编码一摸一样,这样所有原先使用ASCII编解码的文档就可以直接转到UTF-8编码了。对于其他字符,则使用2-4个字节来表示,其中,首字节前置1的 数目代表正确解析所需要的字节数,剩余字节的高2位始终是10。例如首字节是1110yyyy,前置有3个1,说明正确解析总共需要3个字节,需要和后面 2个以10开头的字节结合才能正确解析得到字符

 

 

 

 

 

分享到:
评论

相关推荐

    编码问题 C++ 编码问题 C++

    ):设有一个数组A:array [0..N-1] of integer; 存放的元素为0~N-1(1)之间的整数,且A[i]≠A[j](i≠j)。...要求编程解决以下问题: (1)给出数组A后,求出其编码; (2)给出数组A的编码后,求出A中的原数据

    关于编码问题的深度解析

    编码问题的产生其实都是I/O操作导致的问题,也就是说所有I/O存在的地方就有可能会出现编码问题。 所以要想深度解析编码问题,我们就必须回到源码的实现机制,找到I/O操作中可能导致乱码的操作(行为)。我们知道I/O...

    关于代码编码问题

    关于代码编码问题

    CMMI三级认证 编码 PI访谈问题与答案个人总结整理

    CMMI三级认证 编码 PI访谈问题与答案个人总结整理CMMI三级认证 编码 PI访谈问题与答案个人总结整理CMMI三级认证 编码 PI访谈问题与答案个人总结整理CMMI三级认证 编码 PI访谈问题与答案个人总结整理CMMI三级认证 ...

    深入分析Java中的中文编码问题

    编码问题一直困扰着开发人员,尤其在Java中更加明显,因为Java是跨平台语言,不同平台之间编码之间的切换较多。本文将向你详细介绍Java中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别...

    光电编码器的常见问题

    这是关于光电编码器的一些常见问题的注意,预防,以及处理。

    Huffman树编码问题

    关于Huffman树的编码问题,适合基础薄弱的学生转载

    java字符集编码问题

    字符集编码问题字符集编码问题字符集编码问题

    多元Huffman编码问题(贪心算法)

    题目:在一个操场的四周摆放着n堆石子。现要将石子有次序的合并成一堆。规定每次至少选2堆最多选k堆石子合并成新的一堆,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n堆石子合并成一堆的最大费用和最小...

    jsp页面文档编码问题

    总结了在java web开发中遇到的种种编码问题,使乱码问题逐一清除

    数据结构之哈夫曼编码问题

    哈夫曼编码问题能够处理文件的压缩与还原。

    论文研究-规划问题编码成SAT问题研究.pdf

    讨论了如何将规划问题编码成命题可满足问题的一般技术,并对“直接编码”和“基于规划图的编码”两种编码技术进行了比较,指出了两种编码技术各自的优缺点。在此基础上,深入地分析了各种不同的编码方案之间的异同点...

    编码问题介绍

    jsp乱码介绍Java中Unicode、编码的理解 Java号称国际化的语言,是因为它的class文件采用UTF-8,而JVM运行时使用UTF-16。因此java用的都是Unicode。 unicode 的目标就是能支持世界上所有的字符集,也就是说几乎所有...

    cxf使用中编码问题

    cxf使用中编码问题,有关于gbk等的编码问题在cxf中的解决方法

    贪心算法解哈夫曼编码问题

    这是根据算法设计与分析的课程实验而编写的代码,完全可以使用,欢迎大家下载。

    汉字编码问题总结

    gb,unicode,big5三种可能的汉字编码总结

    哈弗曼编码问题的解决

    C++实现的哈弗曼问题解决,包括哈弗曼编码、译码等的代码实现

    java-web 编码问题详解

    java-web 编码问题详解,内容很详细的解决各种编码问题、

    汉字编码问题编码问题

    GB2312编码 Unicode编码 Big5 编码规则,等相关

Global site tag (gtag.js) - Google Analytics