ZFIVE5[醉马不肖]

变 发奋编程、发奋设计和发奋探究

赵子ID:zfive5
275065次访问,排名197好友18人,关注者67
zfive5的文章
原创 258 篇
翻译 0 篇
转载 168 篇
评论 201 篇
ZFIVE5的公告

年迈的阿基米德,还有牛顿与高斯, 尽管他们理应跻身至尊三清。 在数学那荣耀的殿堂里, 不妨为另一位大师留出一席。
最近评论
King:太谢谢了,解决了一直困扰我的问题
w1shi2wei3:说的 对
海贝:打不开啊!!
蓝奇高级验证码识别引擎QQ:631753663:出售蓝奇高级验证码识别引擎,可准确识别新浪动网淘宝CSDN等多种复杂验证码。

输出为一个标准DLL,可供VB,VC,Delphi,C#.NET,VB.NET,模拟精灵,按键精灵等多平台调用,调用方法简单,几行代码即可完成。独具特色的边缘检测字符分离、旋转倾斜纠正和通用字符匹配算法(无论字体和大小), 使得该引擎对于像新浪、动网、淘宝、CSDN等多种验证码均有不错的识别率……
everyman1978:长见识了
文章分类
收藏
相册
工作生活
名画欣赏
意味深长
追忆过去
GOOD BLOG
Cnblogs 首页原创.NET区
DentistryDoctor(牙医的目标是没有蛀牙)
Eagle-休闲一生
Google 黑板报
Robin's Blog
sodme(网络)
W2
zqqa
ξσ Dicky's Blog σξ
中国协议分析网
乌龙
凡夫俗子
博客堂
思归呓语
智慧的鱼(aoosang)的专栏
测试wendy
源代码搜索引挚
白吃
老木土
自由呼吸
陆其明(RSS)
非程序
朋友
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

转载  编码又见编码 收藏

新一篇: 我的第一个AJAX程序 | 旧一篇: TCP/IP Illustrated

前几天,Google给我Hotmail邮箱发了封确认信。我看不懂,不是因为我英文不行,而是"???? ????? ??? ????"的内容让我不知所措。有好多程序员处理不好编码问题。不是因为他们学不会,而是因为他们太保守或太不以为然了!我想说,初级程序员需要积累更多的计算机高级知识;高级程序员需要了解更多的底层知识。
  那么Content-Type标记到底有什么作用?UTF-8与Unicode到底有何关系?…………现在我们就一起来揭开编码那神奇的面纱!

从ASCII编码谈起:
  我们需要了解的最早编码是ASCII码。它用7个二进制位来表示,由于那个时期生产的大多数计算机使用8位大小的字节,因此用户不仅可以存放所有可能的ASCII字符,而且有整整一位空余下来。如果你技艺高超,可以将该位用做自己离奇的目的:WordStar中那个发暗的灯泡实际上设置这个高位,以指示一个单词中的最后一个字母,同时这也宣示了WordStar只能用于英语文本。
  由于字节有多达8位的空间,因此许多人在想:“呀!我们可以把128~255之间的编码用做个人的应用目的。”问题在于,同时产生这种想法的人相当多,而且在128~255之间的各个位置上应该存放什么这一问题上,真是仁者见仁智者见智。事实上,只要人们开始在美国以外的地方购买计算机,那么各种各样的不同OEM字符集都会进入规划设计行列,并且各人都会根据自己的需要使用高位的128个字符。如此一来,甚至在同语种的文档之间就不容易实现互换。ASCII可被扩展,最优秀的扩展方案是ISO 8859-1,通常称之为Latin-1。Latin-1包括了足够的附加字符集来写基本的西欧语言。
    最后,这个人人参与的OEM终于以ANSI标准的形式形成文件。在ANSI标准中,每个人都认同如何使用低端的128个编码,这与ASCII相当一致。不过,根据所在国籍的不同,处理编码128以上的字符有许多不同的方式。这些不同的系统称为代码页。
  同时,甚至更为令人头疼的事情正在逐步上演,亚洲国家的字符表有成千上万个字符,这样的字符表是用8位二进制无法表示的。该问题的解决通常有赖于称为DBCS(double byte character set,双字节字符集)的繁杂字符系统。
  不过,仍然需要指出一点,多数人还是姑且认为一个字节就是一个字符,以及一个字符就是8个二进制位,并且只要确保不将字符串从一台计算机移植到另一台计算机,或者说一种以上的语言,那么这几乎总是可以凑合。当然,只要一进入Internet,从一台计算机向另一台计算机移植字符串就成为家常便饭了,而各种复杂状况也随之呈现出来。令人欣慰的是,Unicode随即问世了。

Unicode编码:
  Unicode勇往直前地创建一种单一字符集,试图囊括地球上所有合理文字体系。每个字符在Unicode中一律以2个Byte编码。ISO颁布10646号标准叫UCS(Universal Character Set)。在UCS通用集中,每个字符用4个Byte编码。庆幸的是,Unicode策进会自欺欺人1991年以来便和UCS小组密切配合,从而使Unicode和ISO 10646保持一致辞。
  仔细算来,康熙字典里的中文字就有4万多个,如果再加上里面没有的简体字,那么Unicode的6万字容量仅汉字就已用得大部分。对此,Unicode和UCS采用中、日、韩整合(CJK Unification)的解决方案,把中日韩文中笔划相近的字,尽量以一个单码来代表。经过中日韩整合的汉字,在Unicode中称Unihan(统汉字)。Unicode标准中,共有2万多个统汉字,所以它不包括日常罕见的汉字。Unihan统汉字在Unicode中主要分布在3400到9fff之间,此外f900和faff之间了有一些。其中BIG5和GB2312中的汉字和符号都在4000和9fff之间。
  UCS通用字符集中,有UCS-2和UCS-4等编码方式,其中的'2'和'4'指的是字节数。就目前而言,在UCS-2码之前补上两个零字节,便可得到相对应的UCS-4码。

UTF-8编码与UTF-16编码:
  UCS只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个编码;也可以用utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。UTF是"UCS Transformation Format"的缩写。
  用Unicode字符集写的英语文本是ASCII或Latin-1写的文本大小的两倍。UTF-8是Unicode压缩版本,对于大多数常用字符集(ASCII中0~127字符)它只使用单字节,而对其它常用字符(特别是朝鲜和汉语会意文字),它使用3字节。如果写的主要是英语,那么UTF-8可减少文件大小一半左右。反之,如果主要写汉、日、朝鲜语,那么UTF-8会把文件大小增大50%。UTF-8就是以8位为单元对UCS进行编码。
  除非特别指定,XML处理器假设文本数据是UTF-8格式。
UTF-8的格式为:

UCS-2编码(16进制) UTF-8 字节流(二进制)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 10xxxxxx
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx

例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。


在当今大多数计算机系统中,仍是以字节单元来做存取。如果以UTF-16(双字节)来存取资料,会产生许多问题。例如,在C语言中,0x00这个字节有特殊的意义和作用。而UTF-16的头256个字码的第一个字节,正是0x00(第二个字节则是相对的ISO 8850-1字码),因此用UTF-16来表示文档名、网址等,会出现许多意想不到的问题。不只0x00,还有许多其他字符,也都可能和许多OS相冲突。
  此外,UTF-16及许多亚洲地区现行的双字节区域性编码方式,在应用上都有先天性缺陷。例,字符与字符之间的边界不好找,程序在处理时必须从头扫描,才能正确可靠地找出某个字符的边界,这样效率极低。此处,若中文文件中某个地方错了一个字节,所有在这个字节之后的中文字都会坏掉,一直到下个英文字符或空白字符出现为止。
  以上问题,在UTF-8中都不存在。Unicode标准中明文规定,当UTF-8格式的文件中有不合法的组合出现时,该怎么处理。例,碰到第一个字节是以1110开头,但是下一个字节却是以0开头。
  从另一个角度看,若改用UTF-16来编码,虽然不会增加亚洲文字所占的空间(都是两个字节),但对西文来说,等于让所有的文件大小一律加倍。这也难怪西方世界的软件设计师对Unicode缺乏兴趣。有了UTF-8,为数众多的英文文件不需任何转换,就自然符合UTF-8,这对向英文国家促销Unicode有很在帮助。

编码序的问题:
  将编码存储为2个字节的传统方法称为UCS-2(因为它有2个字节)或者UTF-16(因为它有16位),不过仍然需要弄清它是高端存放模式的UCS-2,还是低端存放模式的UCS-2。
  big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。如果将49写在前面,就是little endian。

  "endian"这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,一个皇帝送了命,另一个丢了王位。

  我们一般将endian翻译成"字节序",将big endian和little endian称作"大尾"和"小尾"。
  UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如"奎"的Unicode编码是594E,"乙"的Unicode编码是4E59。如果我们收到UTF-16字节流"594E",那么这是“奎”还是"乙"?
  Unicode规范中推荐的标记字节顺序的方法是BOM(即Byte Order Mark)。
  BOM是一个有点小聪明的想法:

  在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。

  这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

  UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

  Windows就是使用BOM来标记文本文件的编码方式的。

汉字的编码:
  早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的BIG5。

  GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。GB2312-80中共收录了7545个字符,用两个字节编码一个字符。每个字符最高位为0。GB2312-80编码简称国标码。

  GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。

  从ASCII、GB2312到GBK,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK都属于双字节字符集 (DBCS)。
  2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。从汉字字汇上说,GB18030在GB13000.1的20902个汉字的基础上增加了CJK扩展A的6582个汉字(Unicode码0x3400-0x4db5),一共收录了27484个汉字。
  GB18030的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A的6582个汉字。 例如:UCS的0x3400在GB18030中的编码应该是8139EF30,UCS的0x3401在GB18030中的编码应该是8139EF31。

微软提供了GB18030的升级包,但这个升级包只是提供了一套支持CJK扩展A的6582个汉字的新字体:新宋体-18030,并不改变内码。Windows 的内码仍然是GBK。
  这里还有一些细节:
  GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
  前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。

  对于任何字符编码,编码单元的顺序是由编码方案指定的,与endian无关。例如GBK的编码单元是字节,用两个字节表示一个汉字。 这两个字节的顺序是固定的,不受CPU字节序的影响。UTF-16的编码单元是word(双字节),word之间的顺序是编码方案指定的,word内部的字节排列才会受到endian的影响。后面还会介绍UTF-16。

  GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。
  目前Windows的内核已经采用Unicode编码,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。

  Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为下节提到的内码。GBK对应的code page是CP936。

  微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个code page是无法真正使用的。


内码与外码:
  我们常说汉字的"内码"与"外码"。内码是汉字在计算机内部存储,处理和传输用的信息编码。它必须与ASCII码兼容但又不能冲突,所以把国标码两个字节的最高位置'1',以区别于西文,这就是内码。汉字的输入码称为"外码"。输入码即指我们输入汉字时使用的编码。常见的外码分为数字编码(如区位码),拼音编码和字形编码(如五笔)。
    再说区位码,"啊"的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码,我们在区位码的高、低字节上分别加上A0。这样"啊"的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然GB2312的原文根本没提到这一点。 
  内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的.现在的Windows在内部统一使用Unicode,然后用代码页适应各种语言,"内码"的概念就比较模糊了。我们一般将缺省代码页指定的编码说成是内码。内码这个词汇,并没有什么官方的定义。代码页也只是微软的一种习惯叫法。作为程序员,我们只要知道它们是什么东西,没有必要过多地考证这些名词。
  所谓代码页(code page)就是针对一种语言文字的字符编码。例如GBK的code page是CP936,BIG5的code page是CP950,GB2312的code page是CP20936。
  Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows应该去怎么解释它呢?是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解释?如果按GBK去解释,就会得到"汉字"两个字。按照其它编码解释,可能找不到对应的字符,也可能找到错误的字符。所谓"错误"是指与文本作者的本意不符,这时就产生了乱码。
  答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI,其实就是按照缺省代码页的编码方法保存。

  Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可以指定charset。

  有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会出现乱码。这时只要在这个html文件中加上指定charset的语句,例如:
  <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
如果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。                         

进一步的参考资料
"Short overview of ISO-IEC 10646 and Unicode" (http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html

发表于 @ 2006年03月08日 19:25:00|评论(loading...)|编辑

新一篇: 我的第一个AJAX程序 | 旧一篇: TCP/IP Illustrated

评论:没有评论。

发表评论  


登录
Csdn Blog version 3.1a
Copyright © ZFIVE5