文章

XML的编码方式(上)

  近来很多人问我,如何使 XML 文件在不同的平台间正确地传递数据。他们创建了 XML 文档,键入了数据,粘上了几个标记,调整了标记的格式,甚至放入了 $#@60;?xml version="1.0"?$#@62; 声明,作为额外增添。然后他们试着装载它,可得到的却是意想不到的出错消息,Microsoft(R) XML Parser (MSXML) 报告说数据有问题。对初编 XML 者来说,这真令人沮丧。难道它竟不能正常工作吗?

  当然不是。当从 MSXML 收到意想不到的出错消息时,很可能是因为接收数据的平台将其存储到了与发送数据不同的平台上,结果导致了字符编码问题。

跨平台数据格式
  自从计算机软件和硬件从业人员设法将两台计算机连接到一起以来,他们就一直向着创建跨平台技术并使不同的平台能够共享数据的领域而努力奋斗。很早以来,由于不同类型的计算机的数量、连接方式、希望共享的数据种类的急剧增加,事态也就变得越来越复杂。

  经过几十年关于跨平台编程技术的研究,当今(而且可能在未来的很长一段时期内)唯一的真正跨平台解决方案是通过简单的标准数据格式得到的。Web 的成功正是建立在这种格式上的。在 Web 服务器和 Web 浏览器之间传递的主要内容是 HTTP 标题和 HTML 页,两者都是标准的文本格式。

  在以下几节中,我将讨论字符编码和标准字符集、Unicode、HTML Content-Type 标题、HTML Content-Type 元标记和字符实体。如果您熟悉上述概念,可跳过这些内容去读 encoding XML data for the XML Document Object Model (DOM) programmer(针对 XML 文档对象模型 (DOM) 编程人员的编码 XML 数据)的提示和技巧。有关详细信息,请参阅 XML and Character Encoding(XML 和字符编码)。

关于字符编码
  标准文本格式是建立在标准字符集之上的。要记住,所有的计算机均将文本存储为数字。然而,不同的系统也可以用不同的数字存储相同的文本。下表显示了一组字节是如何被存储的,第一个是使用默认代码页 1252、运行 Microsoft Windows(R) 的典型计算机,第二个是使用 Macintosh Roman 代码页的典型 Apple(R) Macintosh(R) 计算机。


























Byte Windows Macintosh
140 Œ å
229 å Â
231 ç Á
232 è Ë
233 é È


  比方说,如果您的祖母从 http://www.barnesandnoble.com/(英文)订购了一本新书,她不会想到她的 Macintosh 计算机存储字符的方式,并不同于运行 www.barnesandnoble.com(英文)的新 Windows 2000 Web 服务器。在往 Internet 订购单的发货栏中输入瑞典家中的地址时,她相信 Internet 会正确地传递字符 å(在其 Macintosh 上的字节值是 140),并没想到接收和处理她发送消息的计算机会将字节值 140 转换为字母 Œ。

Unicode
Unicode Consortium(统一码协会)确信(用双字节而不是单字节表示每个字符)定义一个通用的代码页是个好主意,该代码页适用于全世界所有的语言,从而不同代码页之间的映射问题将不复存在。

  既然如此,如果 Unicode 解决了跨平台的字符编码问题,那为何它却未成为唯一的标准呢?第一个问题是,转换到 Unicode 有时意味着使所有的文件大小加倍 — 这样做在网络世界中是不可想象的。因此有人仍乐于使用老的、单字节的字符集,如 ISO-8859-1 到 ISO-8859-15、Shift-JIS、EUC-KR 等等。

  第二个问题是,仍存在许多并非基于 Unicode 的系统,这就意味着在网络上,某些组成 Unicode 字符的字节值可能会给那些更旧的系统造成严重问题。因此定义了“Unicode 转换格式 (UTF)”;它们运用位转换技术对 Unicode 字符进行编码,使其成为在老系统上“透明的”(或可安全通过)的字节值。

  此类字符编码中最普及的是 UTF-8。UTF-8 采用 Unicode 标准的前 127 个字符(它们恰好是基本的拉丁文字符:A-Z、a-z 和 0-9,以及几个标点字符),并直接将其映射到单字节值。然后采用位转换技术,用字节的高位来编码 Unicode 字符的其余部分。这样做的结果是,小瑞典字符 å (0xE5) 变成了下列双字节乱码:Ã¥ (0xC3 0xA5)。所以,除非您能够在脑海里进行位转换,否则,在UTF-8 中编码的数据是无法被人读懂的。

Content-Type 标题
  因为更旧的单字节字符集仍被使用,所以只有当指定了数据所在的实际字符集之后,传输数据的问题才能得以解决。认识到这一点后,Internet 电子邮件和 HTTP 协议小组定义了一种标准方法,用以在消息标题 Content-Type 属性中指定字符集。该属性从注册的字符集名称列表中指定一个字符集,该字符集名称是由 Internet Assigned Numbers Authority (IANA)定义的。典型的 HTTP 标题都可能包含下列文本:

HTTP/1.1 200 OK
Content-Length: 15327
Content-Type: text/html; charset:ISO-8859-1;
Server: Microsoft-IIS/5.0
Content-Location: http://www.microsoft.com/Default.htm
Date: Wed, 08 Dec 1999 00:55:26 GMT
Last-Modified: Mon, 06 Dec 1999 22:56:30 GMT

  该标题向应用程序表明,跟在标题后面的内容位于 ISO-8859-1 字符集中。

Content-Type 元标记
Content-Type 属性是可选项,在有些应用程序中,HTTP 标题的信息被去掉了,而只有 HTML 本身通过。为了补救这一点,HTML 标准小组定义了一种可选的元标记方法,用于指定 HTML 文档本身的字符集,使 HTML 文档字符集是自描述的。

$#@60;META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1"$#@62;

  在这种情况下,字符集 ISO-8859-1 说明在此特定的 HTML 页中,字节值 229 表示 å。现在该页对任何系统来说,都是完全清楚的,数据不会被曲解。遗憾的是,由于此元标记是可选的,所以它给错误留下了空子。

字符实体
  不是所有的系统支持所有的注册字符集。例如,我并不认为很多平台实际上可支持称为 EBCDIC 的 IBM 主机字符集。Windows NT 是支持的,但许多其他系统很可能不支持 — 这大概就是 http://www.ibm.com/(英文)主页为什么生成 ASCII 的原因。

  作为备选方案,HTML 允许通过指定确切的 Unicode 字符值,对该页中的单个字符进行编码。然后将这些字符实体进行脱离字符集的分析,即可确切地确定其 Unicode 值。它的语法是 ?amp;#229;?or ?amp;#xE5;?。

XML 和字符编码
  XML 从 HTML 那里借鉴了这些思想,并使之更进一步,定义了一个彻底明确的算法,以确定编码使用的字符集。在 XML 中,由 XML 声明中的可选编码属性定义字符编码。下列算法确定默认的编码:

 如果文件以 Unicode 字节次序标志 [0xFF 0xFE] 或 [0xFE 0xFF] 开头,则认为该文档是在 UTF-16 编码中。否则,它在 UTF-8 中。

以下是所有正确和等效的 XML 文档:

























class=90v>字符集或编码 class=90v>HTTP 标题 class=90v>XML 文档
class=90v>ISO-8859-1 Content-Type:
text/xml; charset:ISO-8859-1;
class=90v>$#@60;test$#@62;ålt;/test$#@62;
UTF-8 Content-Type:
text/xml;
class=90v>$#@60;test$#@62;Ã¥$#@60;/test$#@62;
class=90v>ISO-8859-1 Content-Type:
text/xml;
$#@60;?xml
version="1.0"
encoding="ISO-8859-1"?$#@62;
$#@60;test$#@62;ålt;/test$#@62;
class=90v>UTF-8(用字符实体) Content-Type:
text/xml;
class=90v>$#@60;test$#@62;å$#@60;/test$#@62;
UTF-16(带字节次序标志的
Unicode)
Content-Type:
text/xml;
ff fe 3c 00 74
00 65 00 73 00 74 00 3e 00 e5 00
..$#@60;.t.e.s.t.$#@62;...
3c 00 2f 00 74 00 65 00 73 00 74
00 3e 00 0d 00 $#@60;./.t.e.s.t.$#@62;...
0a
00