2008年7月2日星期三

XML_002:encoding属性里面的学问 (摘录+整理)

XML文档第一行一般是XML声明,虽然不是必须的,但还是建议包含这行声明。形式如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
其中standalone属性表示是否需要导入其它文件,这是一个优化选项,如果为“yes”,希望解析器会做一些优化处理。默认为“no”。
encoding属性是我们要谈论的重点,有两个问题必须予以面对:

问题1:哪些值可以作为encoding的属性值?
问题不难,但Google了一圈,竟然没有找到答案!

问题2:为什么要有这个属性?
嘿嘿,说到这个问题,那可就要从字符与编码的发展说起,内容很多很杂,如果你没耐心,还是去别处玩耍吧。

1. 字符与编码的发展
从计算机对多国语言的支持角度看,大致可以分为三个阶段:
阶段一:系统内码为ASCII
【ASCII】:American Standard Code for Information Interchange 美国标准信息交换标准码。
【ANSI】:American National Standard Institute 美国国家标准学会。
【ISO】:International Organization for Standardization 国际标准化组织。
ASCII由ANSI在1967年最终制定的,标准的单字节字符编码方案,用于基于文本的数据。被ISO定为国际标准,称为ISO 646标准。
ASCII使用指定的7位二进制数[0x00~0x7F]来表示128种可能的字符:所有的大写和小写字母,数字0~9、标点符号,以及在美式英语中使用的特殊控制字符。
此时计算机只支持美式英语,其它语言不能够在计算机上存储和显示。
代表系统:英文DOS。
阶段二:系统内码为ANSI编码(本地化)
ASCII是美国标准,它甚至不能满足其它英语国家的需要(如英镑符号£),何况亚洲国家。
于是人们开始扩展ASCII ,扩充使用 [0x80~0xFF]来表示各自国家的语言字符。其中ISO 8859系列就是欧洲语言的扩展字符集。比如:


  • ISO 8859-1 (Latin-1) 西欧语言

  • ISO 8859-2 (Latin-2) 中欧语言

  • ISO 8859-3 (Latin-3) 南欧语言

  • ISO 8859-4 (Latin-4) 北欧语言

  • ISO 8859-5 (Cyrillic) 斯拉夫语言

  • ISO 8859-6 (Arabic) 阿拉伯语

  • ISO 8859-7 (Greek) 希腊语

  • ISO 8859-8 (Hebrew) 希伯来语

亚洲国家同样扩充使用[0x80~0xFF],不过是用2个bytes来表示1个字符。比如:汉字 “中”在中文操作系统中,使用[0xD6,0xD0]这2个字节存储。依此类推,亚洲国家产生了各自的编码标准。这些使用2个bytes来代表1个字符的各种汉字延伸编码方式,统称为ANSI编码。比如:


  • GB2312,简体中文字符集的中国国家标准,在ANSI中的代码页(Code Page)是CP936,收录7445 个字符,包括6763个汉字和682个符号。GB2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。对于人名、古汉语等方面出现的罕用字,GB2312不能处理,这导致了后来GBK以及GB18030字符集的出现。

  • BIG5编码 繁体中文。

  • JIS编码 日文。

在简体中文系统下,ANSI编码代表GB2312编码,在日文操作系统下,ANSI编码代表JIS编码。
显然,不同ANSI编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段ANSI 编码的文本中。比如:我们无法做到在浏览器上正确显示一个同时包含中日韩美法德文字的网页(有些变态,呵呵)。
代表系统:中文DOS,中文Windows 95/98,日文Windows 95/98 。
阶段三:系统内码为UNICODE(国际化)
为了促进国际间信息交流,
Unicode Consortium制定了UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换和处理的要求。这下可真是太好了,可以正确显示同时包含中日韩美法德的网页了!想想,同样的道理,在Google上能用简体中文查到繁体中文,甚至日文的结果。
代表系统:Windows NT/2000/XP,Linux 。
为了有更直观的感受,我们来有必要了解一下在三个阶段中,字符串在内存中是如何存放的:
◆在ASCII阶段,1个字符用1个字节表示。比如,“Bob123” 在内存中为:
42 6F 62 31 32 33 00 分别对应 B o b 1 2 3 \0。
◆ 在ANSI 编码阶段,1个字符用1个字节或多个字节来表示。比如,“中文123”在中文Windows 95 内存中为7个字节,1个汉字占2个字节,1个英文和数字占1个字节:
D6 D0 CE C4 31 32 33 00 分别对应 中 文 1 2 3 \0。
◆在UNICODE阶段,每个字符用在UNICODE字符集中的序号表示。目前计算机一般使用2个字节来存放一个序号(DBCS:double-byte character set )。比如,字符串“中文123”在Windows 2000下,内存中实际存放的是5个序号,一共占10个字节:
2D 4E 87 65 31 00 32 00 33 00 00 00 分别对应 中 文 1 2 3 \0。
注意,在x86 CPU中,低字节在前。

2. 字符,字节,字符串
(1)字符:人们使用的记号,抽象意义上的一个符号。如'1','中','a','$','¥'等。对应于Java语言中的char类型 1个char=2个bytes,其值就是UNICODE 中的序号
(2)字节:计算机数据存储单元,一个8位的二进制数。如0x01, 0x45, 0xFA等。对应于Java语言中的byte类型。
(3)ANSI字符串:在内存中,如果“字符”是以 ANSI 编码形式存在的,1个字符可能使用1个或多个字节来表示,那么我们称这种字符串为ANSI 字符串或者多字节字符串。比如:"中文123"(占7个字节)。对应于Java语言中的byte[]类型。
(4)UNICODE字符串:在内存中,如果“字符”是以在UNICODE 中的序号存在的,那么我们称这种字符串为UNICODE 字符串。如"中文123"(占10个字节)。 对应于Java语言中的String类型。
由于不同ANSI 编码所规定的标准是不相同的,因此,对于一个给定的多字节字符串,我们必须知道它采用的是哪一种编码规则,才能够知道它包含了哪些“字符”。相当于在Java语言中,作“字节串→字符串的转换” string = new String(bytes, "encoding")。而对于一个给定的UNICODE 字符串来说,不管在什么环境下,它所代表的“字符”内容总是不变的,至于“字符”内容所对应的字节有几个,是哪些字节,取决于你按哪种编码规则转换。相当于在Java语言中,作“字符串→字节串”的转换,bytes = string.getBytes("encoding")。
3. 字符集与编码(1)字符集:所包含“字符”的集合。比如“UNICODE 字符集”包含了各种语言中使用到的所有“字符”。
(2)编码:规定每个“字符”分别用1个字节还是多个字节存储,用哪些字节来存储,这个规则叫做“编码”。比如:常见的UNICODE 字符集编码有UTF-8,UTF-16。

4. UTF-8 编码原理


【UTF】:Unicode Transformation Format 通用字集变换格式,推荐使用UTF-8和UTF-16。其中8和16指的是bits,不是bytes。
【UTF-16】:基本上就是Unicode的双bytes编码的实现,再加上一个应付未来扩充需要的编码机制。
【UTF-8】:一种不等幅的编码方式。英文不需要转换,其他语言则需要转换,而且会变“胖”,因为每个字符需要多用1到2个bytes来编码。

UTF-8共有三种可能的编排方式,各自需要用到1、2、3个bytes,所以说它是不等幅。从下图我们可以看出:
1. U+0000~U+007F 用单byte表示所有的ASCII,且第一个bit是0,所以所有的ASCII码都不需要转换,就是自然相对的UTF-8码;反过来说,只要不是ASCII码,在UTF-8就需要2个或2个以上的bytes来编码表示。
2. U+0080~U+07FF
3. U+0800~U+FFFF

UTF-8三种形式中提供的自由bits(蓝色小方块)数,刚刚好对应该区位中的各个Unicode码。
那么当程序处理UTF-8编码的文件时,如何得知它到底是哪一种形式呢?
答案是上图中的绿色小方块。每个以UTF-8编码的字符,第1个byte的前端都表明了该字符的byte总数。而且每个多重byte的UTF-8编码的共性是,其第2个或第3个byte,一律以10这两个bits打头,很容易和只用到一个byte的ASCII码区分开来。
我们知道汉字“你”的Unicode是0x4F60,据此我们可以推导出它的UTF-8码是E4 BD A0。推导过程如下:
0x4F60的二进制如下:
0100 1111 0110 0000
用UTF-8第3种方式补齐,变成:1110 0100 1011 1101 1010 0000,于是得到E4 BD A0。


很显然,使用UTF-8编码会使中、日、韩文变胖,变成原来的150%(针对GB2312而言)。如果使用UTF-16编码,虽然不会增加亚洲文字的空间,但对于西文来说,等于增加了一倍,变成原来的200%,显然更不好,毕竟,是目前在网络上还是英文占主导地位。所以从这一点来说,UTF-8优点还是大于缺点。


参考文献:
1. 《无废话 XML》 作者:劳虎。
2.
http://www.unicode.org/
3.
http://www.xikao.com/reference/unicode.htm
4.
http://www.chedong.com/tech/hello_unicode.html
5.
http://www-128.ibm.com/developerworks/cn/java/java_chinese/index.html
6.
http://www-128.ibm.com/developerworks/cn/java/l-javachinese/index.html
7.
http://www.cn-java.com/www1/?action-viewnews-itemid-210
8.
http://www.regexlab.com/zh/encoding.htm
9.
http://epasser.aydc.com.cn/article/adp/2/content15103.html
10.
http://www.builder.com.cn/2007/1118/640102.shtml
11.
http://bbs.chinaunix.net/viewthread.php?tid=666899
12.
http://blog.csdn.net/liujin4049/archive/2007/04/10/1559576.aspx
13.
http://book.xker.com/others/web/web/jsp/index3/10.htm

没有评论: