您的位置:首页 > 理论基础 > 计算机网络

字符编码的奥秘(转自http://blog.csdn.net/hbrqlpf/archive/2007/09/24/1798935.aspx)

2007-12-04 17:31 435 查看
计算机中的字是如何处理的?
如 果你用放大镜看一下,可以看出屏幕上的字是由一个一个的像素点组成的,每一个字符用一组像素点拼接出来,这些像素点组成一幅图像,变成了我们的文字,计算 机又是如何将我们的文字保存起来的呢?是用一个个的点组成的图像将文字保存起来的吗?当然不是,让我们从英文开始,由于英文是拼音文字,实际上所有的英文 字符和符号加起来也不超过100个,在我们的文字中存在着如此大量的重复符号,这就意味着保存每个字符的图像会有大量的重复,比如 e 就是出现最多的符号等等。所以在计算机中,实际上不会保存字符的图像。
什么是字符编码?
由 于我们的文字中存在着大量的重复字符,而计算机天生就是用来处理数字的,为了减少我们需要保存的信息量,我们可以使用一个数字编码来表示每一个字符,通过 对每一个字符规定一个唯一的数字代号,然后,对应每一个代号,建立其相对应的图形,这样,在每一个文件中,我们只需要保存每一个字符的编码就相当于保存了 文字,在需要显示出来的时候,先取得保存起来的编码,然后通过编码表,我们可以查到字符对应的图形,然后将这个图形显示出来,这样我们就可以看到文字了, 这些用来规定每一个字符所使用的代码的表格,就称为编码表。编码就是对我们日常使用字符的一种数字编号。
第一个编码表 ASCII
在 最初的时候,美国人制定了第一张编码表 《美国标准信息交换码》,简称 ASCII,它总共规定了 128 个符号所对应的数字代号,使用了 7 位二进制的位来表示这些数字。其中包含了英文的大小写字母、数字、标点符号等常用的字符,数字代号从 0 至 127,ASCII 的表示内容如下:
0 – 31 控制符号
32 空格
33-47 常用符号
48-57 数字
58-64 符号
65-90 大写字母
91-96 符号
97-127 小写字母
注 意,32 表示空格,虽然我们再纸上写字时,只要手腕动一下,就可以流出一个空格,但是,在计算机上,空格与普通得字符一样也需要用一个编码来表示,33-127 共95个编码用来表示符号,数字和英文的大写和小写字母。比如数字 1 所对应的数字代号为 49,大写字母 A 对应的代号为 65, 小写字母 a 对应的代号为 97。所以,我们所写的代码 hello, world 保存在文件中时,实际上是保存了一组数字 104 101 108 108 111 44 32 119 111 114 108 100。我们在程序中比较英文字符串的大小时,实际上也是比较字符对应的 ASCII 的编码大小。
由于 ASCII 出现最早,因此各种编码实际上都受到了它的影响,并尽量与其相兼容。
扩展 ASCII 编码 ISO8859
美国人顺利解决了字符的问题,可是欧洲的各个国家还没有,比如法语中就有许多英语中没有的字符,因此 ASCII 不能帮助欧洲人解决编码问题。
为 了解决这个问题,人们借鉴 ASCII 的设计思想,创造了许多使用 8 位二进制数来表示字符的扩充字符集,这样我们就可以使用256种数字代号了,表示更多的字符了。在这些字符集中,从 0 - 127 的代码与 ASCII 保持兼容,从 128 到 255 用于其它的字符和符号,由于有很多的语言,有着各自不同的字符,于是人们为不同的语言制定了大量不同的编码表,在这些码表中,从 128 - 255 表示各自不同的字符,其中,国际标准化组织的 ISO8859 标准得到了广泛的使用。
在 ISO8859 的编码表中,编号 0 – 127 与 ASCII 保持兼容,编号128 – 159 共32个编码保留给扩充定义的 32 个扩充控制码,160 为空格, 161 -255 的 95 个数字用于新增加的字符代码。编码的布局与 ASCII 的设计思想如出一辙,由于在一张码表中只能增加 95 种字符的代码,所以 ISO8859 实际上不是一张码表,而是一系列标准,包括 14 个字符码表。例如,西欧的常用字符就包含在 ISO8859-1字符表中。在 ISO8859-7种则包含了 ASCII 和现代希腊语字符。
问题出现了!
ISO 的8859标准解决了大量的字符编码问题,但也带来了新的问题,比如说,没有办法在一篇文章中同时使用 ISO8859-1 和 ISO8859-7,也就是说,在同一篇文章中不能同时出现希腊文和法文,因为他们的编码范围是重合的。例如:在 ISO8859-1 中 217号编码表示字符Ù ,而在 ISO8859-7中则表示希腊字符Ω,这样一篇使用 ISO8859-1 保存的文件,在使用 ISO8859-7编码的计算机上打开时,将看到错误的内容。为了同时处理一种以上的文字,甚至还出现了一些同时包含原来不属于同一张码表的字符的新码 表。
大字符集的烦恼
不管如何,欧洲的拼音文字都还可以用一个字节来保存,一个字节由8个二进制的位组成,用来表示无符号的整数的话,范围正好是 0 – 255。
但是,更严重的问题出现在东方,中国,朝鲜和日本的文字包含大量的符号。例如,中国的文字不是拼音文字,汉字的个数有数万之多,远远超过区区 256 个字符,因此 ISO 的 8859 标准实际上不能处理中文的字符。
通过借鉴 ISO8859 的编码思想,中国的专家灵巧的解决了中文的编码问题。
既 然一个字节的 256 种字符不能表示中文,那么,我们就使用两个字节来表示一个中文,在每个字符的 256 种可能中,低于 128 的为了与 ASCII 保持兼容,我们不使用,借鉴 ISO8859的设计方案,只使用从 160 以后的 96 个数字,两个字节分成高位和低位,高位的取值范围从 176-247 共72个,低位从 161 – 254共94这样,两个字节就有 72 * 94 = 6768种可能,也就是可以表示 6768 种汉字,这个标准我们称为 GB2312-80。
6768 个汉字显然不能表示全部的汉字,但是这个标准是在1980年制定的,那时候,计算机的处理能力,存储能力都还很有限,所以在制定这个标准的时候,实际上只 包含了常用的汉字,这些汉字是通过对日常生活中的报纸,电视,电影等使用的汉字进行统计得出的,大概占常用汉字的 99%。因此,我们时常会碰到一些名字中的特殊汉字无法输入到计算机中的问题,就是由于这些生僻的汉字不在 GB2312 的常用汉字之中的缘故。
由 于 GB2312 规定的字符编码实际上与 ISO8859 是冲突的,所以,当我们在中文环境下看一些西文的文章,使用一些西文的软件的时候,时常就会发现许多古怪的汉字出现在屏幕上,实际上就是因为西文中使用了 与汉字编码冲突的字符,被我们的系统生硬的翻译成中文造成的。
不过,GB2312 统一了中文字符编码的使用,我们现在所使用的各种电子产品实际上都是基于 GB2312 来处理中文的。
GB2312-80 仅收汉字6763个,这大大少于现有汉字,随着时间推移及汉字文化的不断延伸推广,有些原来很少用的字,现在变成了常用字,例如:朱镕基的“镕”字,未收 入GB2312-80,现在大陆的报业出刊只得使用(金+容)、(金容)、(左金右容)等来表示,形式不一而同,这使得表示、存储、输入、处理都非常不方 便,而且这种表示没有统一标准。

为了解决这些问题,全国信息技术化技术委员会于1995年12月1日 《汉字内码扩展规范》。GBK向下与GB2312完全兼容,向上支持ISO 10646国际标准,在前者向后者过渡过程中起到的承上启下的作用。GBK 亦采用双字节表示,总体编码范围为8140-FEFE之间,高字节在81-FE之间,低字节在40-FE之间,不包括7F。在 GBK 1.0 中共收录了 21886个符号,汉字有21003个。
GBK 共收入21886个汉字和图形符号,包括:
* GB2312 中的全部汉字、非汉字符号。
* BIG5 中的全部汉字。
* 与ISO 10646相应的国家标准GB13000中的其它CJK汉字,以上合计20902个汉字。
* 其它汉字、部首、符号,共计984个。
微软公司自Windows 95 简体中文版开始支持GBK代码,但目前的许多软件都不能很好地支持GBK汉字。
GBK 编码区分三部分:
* 汉字区 包括
GBK/2 :OXBOA1-F7FE, 收录GB2312汉字6763个,按原序排列;
GBK/3 :OX8140-AOFE,收录CJK汉字6080个;
GBK/4 :OXAA40-FEAO,收录CJK汉字和增补的汉字8160个。
* 图形符号区 包括
GBK/1 :OXA1A1-A9FE,除GB2312的符号外,还增补了其它符号
GBK/5 :OXA840-A9AO,扩除非汉字区。
* 用户自定义区
即GBK区域中的空白区,用户可以自己定义字符。
GB18030 是 最新的汉字编码字符集国家标准, 向下兼容 GBK 和 GB2312 标准。 GB18030 编码是一二四字节变长编码。 一字节部分从 0x0~0x7F与 ASCII 编码兼容。二字节部分, 首字节从 0x81~0xFE, 尾字节从 0x40~0x7E 以及 0x80~0xFE, 与 GBK标准基本兼容。 四字节部分, 第一字节从 0x81~0xFE, 第二字节从 0x30~0x39, 第三和第四字节的范围和前两个字节分别相同。
不一样的中文
中文的问题好像也解决了,且慢,新的问题又来了。
中 国的台湾省也在使用中文,但是由于历史的原因,那里没有使用大陆的简体中文,还在使用着繁体的中文,并且他们自己也制定了一套表示繁体中文的字符编码,称 为 BIG5,不幸的是,虽然他们的也使用两个字节来表示一个汉字,但他们没有象我们兼容 ASCII 一样兼容大陆的简体中文,他们使用了大致相同的编码范围来表示繁体的汉字。天哪! ISO8859 的悲剧又出现在同样使用汉字的中国人身上了,同样的编码在大陆和台湾的编码中实际上表示不同的字符,大陆的玩家在玩台湾的游戏时,经常会遇到乱码的问题, 问题根源就在于,大陆的计算机默认字符的编码就是 GB2312, 当碰到台湾使用 BIG5 编码的文字时,就会作出错误的转换。
由 于历史和文化的原因,日文和韩文中也包含许多的汉字,象汉字一样拥有大量的字符,不幸的是,他们的字符编码也同样与中文编码有着冲突,日文的游戏在大陆上 一样也会出现无法理解的乱码。《中文之星》,《南极星》,《四通利方》就是用于在这些编码中进行识别和转换的专用软件。
互联的时代
在二十世纪八十年代后期,互联网出现了,一夜之间,地球村上的人们可以直接访问远在天边的服务器,电子文件在全世界传播,在一切都在数字化的今天,文件中的数字到底代表什么字?这可真是一个问题。
UNICODE
实际上问题的根源在于我们有太多的编码表。
如果整个地球村都使用一张统一的编码表,那么每一个编码就会有一个确定的含义,就不会有乱码的问题出现了。
实 际上,在80年代就有了一个称为 UNICODE 的组织,这个组织制定了一个能够覆盖几乎任何语言的编码表,在 Unicode3.0.1中就包含了 49194 个字符,将来,Unicode 中还会增加更多的字符。Unicode 的全称是 Universal Multiple-Octet Coded Character Set ,简称为 UCS。
由于要表示的字符如此之多,所以一开始的 Unicode1.0编码就使用连续的两个字节也就是一个WORD 来表示编码,比如“汉”的UCS 编码就是 6C49。这样在 Unicode 的编码中就可以表示 256*256 = 65536 种符号了。
直 接使用一个WORD 相当于两个字节来保存编码可能是最为自然的 Unicode 编码的方式,这种方式被称为 UCS-2,也被称为 ISO 10646,,在这种编码中,每一个字符使用两个字节来进行表示,例如,“中” 使用 11598 来编码,而大写字母 A 仍然使用 65 表示,但它占用了两个字节,高位用 0 来进行补齐。
由于每个WORD 表示一个字符,但是在不同的计算机上,实际上对 WORD 有两种不同的处理方式,高字节在前,或者低字节在前,为了在UCS-2编码的文档中,能够区分到底是高字节在前,还是低字节在前,使用 UCS-2 的文档使用了一组不可能在UCS-2种出现的组合来进行区分,通常情况下,低字节在前,高字节在后,通过在文档的开头增加 FFFE 来进行表示。高字节在前,低字节在后,称为大头在前,即Big Endian,使用 FFFE 来进行表示。这样,程序可以通过文档的前两个字节,立即判断出该文档是否高字节在前。
Endian 这个词出自 《格列佛游记》,小人国的内战就源于吃鸡蛋时要先吃大头 big endian 还是小头 little-endian,并由此发生了内战。
理想与现实
UCS-2 虽然理论上可以统一编码,但仍然面临着现实的困难。
首先,UCS-2 不能与现有的所有编码兼容,现有的文档和软件必须针对 Unicode 进行转换才能使用。即使是英文也面临着单字节到双字节的转换问题。
其次,许多国家和地区已经以法律的形式规定了其所使用的编码,更换为一种新的编码不现实。比如在中国大陆,就规定 GB2312 是大陆软件、硬件编码的基础。
第三,现在还有使用中的大量的软件和硬件是基于单字节的编码实现的,UCS-2 的双字节表示的字符不能可靠的在其上工作。
新希望 UTF-8
为了尽可能与现有的软件和硬件相适应,美国人又制定了一系列用于传输和保存Unicode 的编码标准 UTF,这些编码称为UCS 传输格式码,也就是将 UCS 的编码通过一定的转换,来达到使用的目的。常见的有 UTF-7,UTF-8,UTF-16等。
其中 UTF-8 编码得到了广泛的应用,UTF-8 的全名是UCS Transformation Format 8, 即 UCS 编码的8位传输格式,就是使用单字节的方式对 UCS 进行编码,使 Unicode 编码能够在单字节的设备上正常进行处理。
UTF-8 编码是变长的编码,对不同的 Unicode 可能编成不同的长度
UCS-2 UTF-8
0000-007F 0- 127 0xxxxxxx
0080-07FF 128- 2047 110xxxxx 10xxxxxx
0800-FFFF 2048-65535 1110xxxx 10xxxxxx 10xxxxxx
例如 1 的Unicode 编码是 31 00,在 0-127之间,所以转换后即为 31,而“中”字的UTF-8 Unicode 编码为 11598,转换成 UTF-8则为 e4 b8 ad。
实际上,ASCII 字符用 UTF-8 来表示后,与 ASCII 是完全一样的,美国人又近水楼台的把自己的问题解决了。但其他的编码就没有这么幸运了。
突破障碍 - Unicode 本地编码的转换
UTF-8 编码解决了字符的编码问题,又可以在现有的设备上通行,因此,得到了广泛的使用,
XML 中的问题
XML 的设计目标是实现跨网络,跨国界的信息表示,所以,在XML 设计之初,就规定 XML 文件的默认编码格式就是 UTF-8,也就是说,如果没有特殊的说明,XML文件将被视为 UTF-8 编码。
然 而,大部分的中文编辑软件,是根据操作系统来决定编码的方式的,所以,在写字板中直接输入并保存的文件,将被保存为 GB2312 编码,所以,在读出 XML文件内容时,往往就会出现文件错误的提示了。这种情况会出现在文件中有中文出现的时候,如果没有中文,只有英文信息,就不会出现问题。原因很简单, 有中文时,因为中文的编码并不是UTF-8 编码,所以会造成冲突,没有中文时,英文的编码在GB2312 中与ASCII是兼容的,而ASCII 与UTF-8 是完全一致的,所以不会出现问题。这种情况也包括 UltraEdit 软件。
但时,专业的 XML编辑软件会自动将内容保存为 UTF-8 编码,不会有问题。
默认情况下,XML 的处理程序一般会将内容作为 UTF-8 编码进行处理,所以保存下来的 XML 文件必须要用可以识别 UTF-8 的软件来进行查看,如Windows 的记事本。
Java 的处理
Java 的设计目标是一次编写,到处运行,所以在 Java 的内部对字符的处理采用了 UCS 来处理,因此 Java 的字符类型不再是 C++ 中的一个字节,而使用两个字节来保存一个字符。
但是,我们会发现,在 Java 的文件流中保存为文件后,我们可以直接使用记事本或 UltraEdit 打开察看。
在 这里,Java 采用了一个灵巧的默认转换机制,当需要将内容中的字符保存到文件中时,Java 会自动的查看一下系统的本地编码,系统的本地编码可以在控制面板中查到,然后,自动将 UCS 编码的字符转换为本地编码,并进行保存。当需要从系统的文件系统中读入一个文件时,Java 通过查看系统的本地编码来决定如何识别文件的内容。这样,Java 就可以在内部使用 UCS, 但用户可以直接使用本地编码的文件了。
Java 在相应的方法中,提供了额外的参数,可以让用户自己来指定文件的编码。
Web 中的问题
网页的编码问题主要有两点,一是网页是如何编码的,二是如何告诉浏览器如何编码的。
第一个问题,又可以分成静态页面和动态页面两个问题。
对静态页面,网页的编码要看你保存文件时的编码选项,多数的网页编辑软件可以让你选择编码的类型,默认为本地编码,为了使网页减少编码的问题,最好保存为 UTF-8 编码格式。
对 动态页面,如 Servlet 生成的页面,在 HttpServletResponse 类中有一个方法 setContentType,可以通过参数来指定生成的页面的类型和编码,例如:response.setContentType ("text/html; charset=utf-8");来指定生成的页面的编码类型。
对 jsp 页面可以通过 <%@ page contentType="text/html;charset=gb2312" %> 来指定生成的页面的编码及类型。
第二个问题,如何通知浏览器网页的编码类型
浏 览器收到只是一个字节流,它并不知道页面是如何编码的,因此,需要一个机制来告诉浏览器页面的编码类型,标准的机制是使用 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 来指定页面的编码,当浏览器读取页面遇到这样的指示时,将使用这里制定的编码方式重新加载页面。
否则的话,浏览器将会试图猜出页面的编码类型。
Tomcat 中的中文问题
在 Tomcat 中,经常遇到取回客户端提交的信息是乱码的问题。
当 提交表单的时候,HTML页面的Form标签会使情况变得更为复杂。浏览器的编码方式取决于当前页面的编码设定,对Form标签也照此处理。这意味着如果 ASCII格式的HTML页面用ISO-8859-1编码,那么用户在此页面中将不能提交中文字符。所以,如果你的页面使用的是 utf-8,那么 POST 的时候,也将使用 utf-8 。
由于 Tomcat 是美国人设计的,Tomcat 默认使用ISO8859-1 编码对客户端返回的内容进行解码,由于编码与内容不一致,就会出现乱码的 ??? 出现,根据以上的分析,在服务器端读取客户端回送的内容时,需要首先设定回送内容的编码,然后再进行信息的读取,通过使用 HttpServletRequest 的方法 setCharacterEncoding("utf-8")先行设定信息的编码类型。然后,就可以正确读取内容了。
总结
编 码问题是信息处理的基本问题,但是由于历史和政治的问题,事实上存在着大量不统一的编码方式,造成在信息处理过程中的信息丢失,转换错误等问题,UCS 为问题的解决提供了一个很好的方向,但是,在现在的软件环境中,还没有达到全面地使用。在实际中工作中应尽量采用统一的编码格式,减少编码问题的发生
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐