Chris Lovett
Microsoft Corporation
2000 年 3 月
目录
摘要:本文介绍了字符编码的工作原理,特别是在 XML 和 MSXML DOM 中的工作原理。
近来很多人问我,如何使 XML 文件在不同的平台间正确地传递数据。他们创建了 XML 文档,键入了数据,粘上了几个标记,调整了标记的格式,甚至放入了 <?xml version="1.0"?>
声明,作为额外增添。然后他们试着装载它,可得到的却是意想不到的出错消息,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 OKContent-Length: 15327Content-Type: text/html; charset:ISO-8859-1;Server: Microsoft-IIS/5.0Content-Location: http://www.microsoft.com/Default.htmDate: Wed, 08 Dec 1999 00:55:26 GMTLast-Modified: Mon, 06 Dec 1999 22:56:30 GMT
该标题向应用程序表明,跟在标题后面的内容位于 ISO-8859-1 字符集中。
Content-Type 元标记
Content-Type 属性是可选项,在有些应用程序中,HTTP 标题的信息被去掉了,而只有 HTML 本身通过。为了补救这一点,HTML 标准小组定义了一种可选的元标记方法,用于指定 HTML 文档本身的字符集,使 HTML 文档字符集是自描述的。
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
在这种情况下,字符集 ISO-8859-1 说明在此特定的 HTML 页中,字节值 229 表示 å。现在该页对任何系统来说,都是完全清楚的,数据不会被曲解。遗憾的是,由于此元标记是可选的,所以它给错误留下了空子。
字符实体
不是所有的系统支持所有的注册字符集。例如,我并不认为很多平台实际上可支持称为 EBCDIC 的 IBM 主机字符集。Windows NT 是支持的,但许多其他系统很可能不支持 — 这大概就是 http://www.ibm.com(英文)主页为什么生成 ASCII 的原因。
作为备选方案,HTML 允许通过指定确切的 Unicode 字符值,对该页中的单个字符进行编码。然后将这些字符实体进行脱离字符集的分析,即可确切地确定其 Unicode 值。它的语法是 ?amp;#229;?or ?amp;#xE5;?。
XML 从 HTML 那里借鉴了这些思想,并使之更进一步,定义了一个彻底明确的算法,以确定编码使用的字符集。在 XML 中,由 XML 声明中的可选编码属性定义字符编码。下列算法确定默认的编码:
如果文件以 Unicode 字节次序标志 [0xFF 0xFE] 或 [0xFE 0xFF] 开头,则认为该文档是在 UTF-16 编码中。否则,它在 UTF-8 中。
以下是所有正确和等效的 XML 文档:
字符集或编码 |
HTTP 标题 |
XML 文档 |
ISO-8859-1 |
Content-Type: text/xml; charset:ISO-8859-1; |
<test>ålt;/test> |
UTF-8 |
Content-Type: text/xml; |
<test>Ã¥</test> |
ISO-8859-1 |
Content-Type: text/xml; |
<?xml version="1.0" encoding="ISO-8859-1"?> <test>ålt;/test> |
UTF-8(用字符实体) |
Content-Type: text/xml; |
<test>å</test> |
UTF-16(带字节次序标志的 Unicode) |
Content-Type: text/xml; |
ff fe 3c 00 74 00 65 00 73 00 74 00 3e 00 e5 00 ..<.t.e.s.t.>... 3c 00 2f 00 74 00 65 00 73 00 74 00 3e 00 0d 00 <./.t.e.s.t.>... 0a 00 |
现在,讨论过不同的字符编码方法之后,让我们来看看如何在 MSXML DOM 中加载 XML 文档,以及当碰到模糊编码的字符时,可能收到的出错消息的类型。加载 XML DOM 文档的两个主要方法是 LoadXML 方法和 Load 方法。
LoadXML 方法总是采用只在 UCS-2 或 UTF-16 中编码的 Unicode BSTR。如果将非有效的 Unicode BSTR 的内容传递给 LoadXML,则加载会失败。
Load 方法可将以下内容当作 VARIANT:
Value |
Des cription |
URL |
如果 VARIANT 是 BSTR,则将其理解为 URL。 |
VT_ARRAY | VT_UI1 |
VARIANT 也可以是包含原始编码字节的 SAFEARRAY。 |
IUnknown |
如果 VARIANT 是 IUnknown 接口,则 DOM 文档为 IStream、IPersistStream 和 IPersistStreamInit 调用 QueryInterface。 |
Load 方法实现以下算法,用于确定 XML 的字符编码或字符集。
- 如果 Content-Type HTTP 标题定义了字符集,该字符集则替代 XML 文档本身的所有内容。因为没有 HTTP 标题,所以这显然不适用于 SAFEARRAY 和 IStream 机制。
- 如果有双字节 Unicode 字节次序标志,则它假设编码是 UTF-16。它既可处理大 endian,也可处理小 endian。
- 如果有四字节 Unicode 字节次序标志 (0xFF 0xFE 0xFF 0xFE),则它假设编码是 UTF-32。它既可处理大 endian,也可处理小 endian。
- 否则,它假设编码是 UTF-8,除非它用指定其他一些字符集的编码属性(如 ISO-8859-1、Windows-1252、Shift-JIS 等),找到 XML 声明。
您将看到两个从 XML DOM 返回的指出编码问题的出错消息。第一个通常指出文档中的字符与 XML 文档的编码不匹配:
在文本内容中发现了一个无效字符。
ParseError 对象可告诉您这个捣乱字符在某一行的确切位置,以便使您解决该问题。
第二个出错消息指出您一开始用的是 Unicode 字节次序标志(或调用了 LoadXML 方法),然后编码属性指定了不是双字节编码的编码(如 UTF-8 或 Windows-1250):
不支持从当前编码转换到指定的编码。
另外,您可能调用了 Load 方法,并在一开始使用了单字节编码(没有字节次序标志),但是它随后发现了指定双字节或四字节编码(如 UTF-16 或 UCS-4)的编码属性。
基本原则是不能利用 XML 声明的编码属性,在多字节字符集如 UTF-8、Shift-JIS 或 Windows-1250,与 Unicode 字符编码如 UTF-16、UCS-2 或 UCS-4 之间进行转换,这是因为声明本身必须对每个字符都使用与文档其余部分相同数量的字节。
最后,IXMLHttpRequest 接口提供如下方法,用以访问下载的数据:
Methods |
Desc ription |
ResponseXML |
表示由 MSXML DOM 分析器分析的响应实体(用与 Load 方法相同的规则)。 |
ResponseText |
表示作为字串的响应实体。本方法盲目地解码从 UTF-8 收到的消息实体。这是一个已知问题,应在即将面市的 MSXML Web Release 中得到解决。 |
ResponseBody |
表示作为无符号字节数组的响应实体。 |
ResponseStream |
表示作为 IStream 接口的响应实体。 |
一旦加载了 XML 文档,即可用 DOM 处理 XML 文档,而不必考虑任何编码问题,因为文档是作为 Unicode 存储在内存中的。所有 XML DOM 接口都是基于 COM BSTR 的,后者是双字节的 Unicode 字串。这就是说,您可以从新开始在包含所有 Unicode 字符的内存中建立 MSXML DOM 文档,并且所有组建将会共享该内存中的 DOM,而不会对 Unicode 字符值的意思有任何疑惑。然而,当对其进行保存时,MSXML 将以默认方式按 UTF-8 编码所有数据。例如,假设您进行了以下处理:
var xmldoc = new ActiveXObject("Microsoft.XMLDOM")var e = xmldoc.createElement("test");e.text = "å;xmldoc.appendChild(e);xmldoc.save("foo.xml");
下列 UTF-8 编码文件的结果是:
<test>Ã¥</test>
注意 上述例子只有当在浏览器以外的环境运行时,才有效。由于受到安全限制,在浏览器里调用 Save 方法将不会产生相同的结果。
尽管这看上去有点怪,但却是正确的。下列测试装载了用 UTF-8 编码的文件,并测试 UTF-8 是否被重新解码为 Unicode 字符值 229。它是:
var xmldoc = new ActiveXObject("Microsoft.XMLDOM")xmldoc.load("foo.xml");if (xmldoc.documentElement.text.charCodeAt(0) == 229){ WSc ript.echo("Yippee - it worked !!");}
要想更改 XML DOM Save 方法使用的编码,需要用如下位于文档顶部的编码属性创建 XML 声明:
var pi = xmldoc.createProcessingInstruction("xml", " version='1.0' encoding='ISO-8859-1'");xmldoc.appendChild(pi);
调用 save 方法时,您就会得到以下用 ISO-8859-1 编码的文件:
<?xml version="1.0" encoding="ISO-8859-1"?><test>ålt;/test>
现在,小心不要被 XML 属性迷惑。XML 属性返回 Unicode 字串。如果在创建 ISO-8859-1 编码声明之后,调用 DOMDocument 对象上的 XML 属性,即可取回以下 Unicode 字串:
<?xml version="1.0"?><test>ålt;/test>
请注意这里没有 ISO-8859-1 编码声明了。这是正常的。这样做的原因是使您可以转而用此字串调用 LoadXML,它会起作用。如果它不这么做,LoadXML 会失败并返回出错消息:“不支持从当前编码切换到指定的编码。”
但愿本文有助于解释字符编码的工作原理,特别是在 XML 和 MSXML DOM 中的工作原理。一旦您理解了字符集编码,它是相当简单的,而且 XML 是非常出色的,因为它在这方面未留有丝毫含糊的余地。尽管 MSXML DOM 有几处怪异需要密切留意,但它仍不失为一个能让您读取和写入任何 XML 编码的强大工具。
- Microsoft MSDN Online Library: XML DOM Reference(Microsoft MSDN 联机库:XML DOM 引用)
- Character Encoding Model(字符编码模型),作者:Ken Whistler 和 Mark Davis
- IANA Character Sets(IANA 字符集)
-
http://www.ietf.org(英文)的 Internet Engineering Task Force (IETF) 提供了 RFC 列表
- Microsoft MSDN Online Library: Compatibility Issues with Mixed Environments(Microsoft MSDN 联机库:与混合环境的兼容性问题)
分享到:
相关推荐
压缩包内有源文件,分别有.net1.1和.net2.0两种,其中Authors.xml为一个模版xml文件,个人需要解析的xml文件要将文件头改为Authors.xml的文件头,即将编码方式改为标准。sqlstring.txt中有针对解析成excel和数据库的...
<? xml版本信息 (编码信息) (文档独立性信息) ?> 如:<?xml version="1.0" encoding="gb2312" standalone="yes"?> 文档类型声明 规定元素的结构和属性列表的取值 如:<!DOCTYPE studinfo SYSTEM ...
TinyXML使用文档对象模型(DOM),这意味着XML数据被解析成一个可被浏览和操作的C++对象,然后它可以被写到磁盘或者另一个输出流中。你也可以把C++对象构造成一个XML文档然后把它写到磁盘或者另一个输出流中。 ...
XML概述 <br>2.1 XML的概念 2.1.1 XML元素 2.1.2 XML属性 2.1.3 XML解析器 2.1.4 构建XML 2.1.5 XML文档的各个组成部分 ...定义 2.3.2 XML Schema 2.3.3 XML编码 2.4 小结 <br>第3章...
我们看上面的例子,在myfile.htm中,我们只关心页面的显示方式,我们可以设计不同的界面,用不同的方式来排版页面,但数据是储存在myfile.xml中,不需要任何改变。 (如果你是程序员,你会惊讶的发现,这与模块化...
概率 XML是描述不确定数据的有效方式, Dewey 编码是一种重要的 XML文档关键字索引编码技术。在概 率 XML 大文档关键字索引检索过程中,频繁地比较关键字索引 Dewey 编码非常耗时。针对上述问题...
为便于理解,Demo程序将解析出的xml数据仍按xml的样子输出到屏幕上,且将"、">"符号换为了"["、"]"。 内附编译好的EXE,用法:ExpatSaxDemo demo.xml 以下便是输出结果: [library] [book format="16开" pages...
首先,我们看一下什么是serializer,serializer就是串行化,又名序列化。它可并不只是简单的把对象保存在存储器上,它...2.设置序列化器的输出路径和编码方式 FileOutputStream = new FileOutputStream(new File(En
鉴于XML关系存储下信息更新的困难性,采用基于order-descendan(O―D)的XML编码方式,不仅可以完全支持XPath查询语言的13个查询轴的查询功能,而且通过简单的计算即可有效地支持XML数据的增加和删除,这种简单计算...
XML Marker是一款XML和JSON编辑器,它使用同步的表格树和文本显示方式向您展示XML数据的层次性和表格性。 它通过收集重复的属性和标签名称,然后将其排列成列,自动生成任何选定标签的表格显示。其结果是一个无杂乱...
但用SAX解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。 3)JDOM http://www.jdom.org JDOM的目的是成为Java特定文档模型,它简化与XML的交互并且比使用DOM实现更快。由于是第一个...
3)将数据序列化位SOAP1.1编码方式或用户定义的数据编码方式。 ×双相编组:1)SOAP解释及编码;2)分解“forward”指针(例如:分解SOAP中的href属性)。 ×完整可定制的SOAP错误处理机制。 ×可定制的SOAP...
这些操作依赖于某些经过编码的数据,例如初始XML声明以及文档的打开和关闭标签,包括名称空间声明。 由于包含所有代谢物数据的XML文档的大小,使用Ruby来利用File.foreach方法的易用性,以最大程度地提高速度并最小...
在项目的 pom.xml 文件中添加Redis依赖 2.配置Redis连接信息: 在 application.properties(或 application.yml)中配置Redis连接信息 3.创建RedisRepository接口: 创建一个继承自org.springframework.data....
DWR.xml配置文件说明书 1、 建立dwr.xml 配置文件 任何一个dwr.xml的文件都需要包含DWR DOCTYPE的声明行,格式如下: <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" ...
lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高 XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言,它最初是用来搜寻XML文档的,但是它...
本软件用于生成行政区划XML文件。使用方式是:首先从国家统计局官网上(http://www.stats.gov.cn/zjtj/tjbz/xzqhdm/)把行政区划代码复制到...从网站上复制数据时,不要复制不相关文字,以UTF-8编码存放在txt文件即可。
XML: 概念:Extensible Markup Language 可扩展标记语言 可扩展:标签都是自定义的。...encoding:编码方式。告知解析引擎当前文档使用的字符集,默认值:ISO-8859-1 standalone:是否独立 取值: yes:不依赖其
FOR 子句 FOR 子句用于指定 BROWSE 或 XML 选项(BROWSE 和 XML 是不...指定查询返回二进制 base64 编码格式的二进制数据。使用 RAW 和 EXPLICIT 模式检索二进制数据时,必须指定该选项。这是 AUTO 模式中的默认值。
Base64其实是一种简单的置换加密方式,但是BASE64的用处往往并不是为了防止信息泄露,而且为了方便传输,进过BASE64编码后的信息会比原始信息长,大概是4/3倍。 Base64是一种基于64个可打印字符来表示二进制数据的...