选择和识别XML字符编码的方式

发表于:2007-05-25来源:作者:点击数: 标签:编码xml识别方式字符
在存储、传输或处理文本时,有必要了解字符编码方式。这种规则也适用于 XML,因为 XML 是基于文本的。 尽管可以使用多种方法对 XML 进行编码,却并没有关于使用哪种编码方式以及如何进行识别的准则。因此,本技术说明阐述了将字符编码方式应用到 XML 的最佳

  在存储、传输或处理文本时,有必要了解字符编码方式。这种规则也适用于 XML,因为 XML 是基于文本的。
  
  尽管可以使用多种方法对 XML 进行编码,却并没有关于使用哪种编码方式以及如何进行识别的准则。因此,本技术说明阐述了将字符编码方式应用到 XML 的最佳应用。
  
  选择 XML 实体的字符编码方式
  在选择所使用的字符编码方式时,必须首先了解可以选择哪些方式。在涉及到标准规范和执行环境的那些特定 XML 应用程序的情况下,可能将编码方式限定在某个范围内。实际上,在一种特定情况下,可能只有一种选择。
  
  术语“规定编码方式”是指在这种情况下必须使用的编码方式。例如,编程环境的字符串数据类型可能必须包含在预定的编码方式中,如 Java 字符中的 UTF-16 或 SQL 字符数据类型中的数据库字符集。唯一的编码方式经常以不同的方法进行规定。表 1 显示了这类情况的一些其他示例。
  
  表 1. 规定唯一编码方式的示例
  
 

  注意:Oracle" XDK for C/C++ 提供一种特殊的模式,允许以任意的单字节字符编码方式来创建 DOM 树,而 API 以指定编码方式工作。此特性用于优化目的,被看作是例外情况,因为其目的是用于那些已了解数据包含指定字符集中字符的情况。强烈建议您始终使用 xmlinitenc 初始化函数或将 data_encoding 属性指定给 XML 上下文。
  使用规定编码方式是一种最佳应用,因为您不必关注文档内或文档本身的编码信息 — 用户始终了解编码方式。因此减少了出错机率并可以提高效率。
  
  但是即使规定了编码方式,XML 处理器也可能不“了解”它 — 在这种情况下,应用程序必须确保使用规定的编码方式。例如,如果 Java 应用程序有一个 DOM 树必须串行化为 UTF-8 [RFC-3629] 格式的输出流,则在将输出从 Writer 转换到 OutputStream 时,通过明确指定 UTF-8 可以确保该过程的执行。以下伪代码是在 Java Servlet 中指定输出编码方式的一个示例:
  
  /* response is an http servlet response object */
  response.setCharacterEncoding("UTF-8"); // set the output encoding to UTF-8
  PrintWriter w = response.getWriter(); // get the output stream mandated to UTF-8
    :
  /* doc is an instance of an XML */
  doc.print(w);              // the document printed in the specified encoding
  
  同样,如果您的输入必须是 UTF-8 格式,则您的应用程序应该编写为只接受 UTF-8 格式的输入。例如,在 Java 语言中,您可能需要使用构造符创建 InputSource 对象,该构造符使用一个参数来指定输入编码方式。此外,您可以从输入流中创建 InputStreamReader,指定 UTF-8 作为输入编码方式。以下伪代码说明如何在 Java 语言中指定输入编码方式。
  
  InputSource is = new InputSource();    // create an input source
  is.setByteStream(request.getInputStream()); // set the input stream mandated to UTF-8
  is.setEncoding("UTF-8");          // set the mandate encoding to the input source
  parser.parse(is);              // the parser will parse in the specified encoding
  
  常用编程环境的大部分字符串数据类型都将字符编码方式限定在特定的范围中。即使提供多种选择,通常也会有某些约束。例如,标准库和 Oracle 库必须支持用于 C/C++ 中 char 类型的字符编码方式。使用 FORCE_INCODING 标志或 input_encoding 属性来指定规定的或外部指定的输入编码方式。以下伪代码演示了如何为 C 的 XDK 指定规定编码方式。
  
  // parse an input stream in UTF-8 with DOM
  XmlLoadDom(ctx, &err, "stream", in, "input_encoding", "UTF-8", NULL);
  // parse an input stream in UTF-8 with SAX
  XmlLoadSax(ctx, &err, "stream", in, "input_encoding", "UTF-8", NULL);
  // print the document in UTF-8
  XmlSaveDom(ctx, &err, doc, "stream", out, "output_encoding", "UTF-8", NULL);
  
  选择规定字符编码方式
  如果应用程序不需要支持多种编码方式,它可以为其本身规定唯一的编码方式。如果规定了一种编码方式,则这种方式应该是 UTF-8 或 UTF-16 [RFC-2781] — 否则交互操作性将受到严重影响,因为使用文档的 XML 处理器可以不支持其他编码方式。如果需要与 US-ASCII [RFC-20] 兼容,或者需要以串行化格式进行传输或存储,则建议使用 UTF-8。在其他情况下,可能适于使用 UTF-16。
  
  支持多种编码方式
  需要支持多种编码方式的应用程序能够支持 XML 处理器所支持的任何编码方式。所有的 XML 处理器都支持 UTF-8 和 UTF-16。它们通常也支持一些常用的本地编码方式。
  
  虽然 Oracle XML 处理器支持所有常用编码方式以及许多其他编码方式,但建议仅在必要时允许多种编码方式。应用程序不应该包含那些不使用 UTF-8 或 UTF-16 编码方式的 XML 文档,除非知道用户支持该编码方式并且其内容可以使用编码方式表达。例如,如果数据库字符集不是 Unicode,则不赞成在数据库中包含 XML 文档并在数据库字符集中利用它为未知用户提供服务。
  
  为接收各种编码方式的输入实体,应该由 XML 处理器以字节流的形式不加更改地读取输入流。确保将外部提供的编码信息(例如内容类型的 HTTP 标题中的 charset 参数)传递到 XML 处理器,以便在 XML 处理器分析输入时强制为指定的编码方式。这种情况就象指定的编码方式就是规定编码方式一样。
  
  为了以任意编码方式生成输出,需要确保实体带有字符编码信息,其方法是通过外部标记实现,如 HTTP 标题的 charset 参数以及 Oracle Files 或 Oracle XML DB 等信息库中的字符集属性,或者是通过嵌入标记实现(即编码声明)。
  
  外部标记优先于内部标记,因为外部标记更可靠并更易于处理;尽可能不使用内部标记是明智的选择。实际上,先前曾讨论过,因为通常不了解编码方式,所以一般不需要内部标记。
  
  经常由于所需的字符集转换而使声明的编码方式与实际编码方式不一致。例如,如果您将带有编码声明信息的文档插入 CLOB 类型的数据库列中,或通过 Java 字符流来读取它时,其声明不会神奇地转变为实际的值。通过使用 NLS_LANG 设置和 Java 字符数据类型等更高级协议来维护正确的编码方式时,很容易避免这种情况。(Oracle 的 XMLType 数据类型可满足处理多种字符编码方式的预期情况。)
  
  外部与内部编码信息的对比
  字符编码信息的来源可以分为两类:外部或内部。本章讨论二者之间的重要差别。
  
  外部编码信息
  图 1 描述了分析器如何根据一种外部编码信息源 — HTTP 的内容类型标题 — 确定字符编码方式。注意,这里没有使用内部编码信息。
  
 

  当输入是以" HTTP 或者任何可以从外部识别其编码方式的其他形式传入时,应用程序应该完成以下工作中的一项:
  
  将 charset 参数的值传递给分析器
  根据 charset 参数将输入流转换为 Unicode
  指示分析器来分析 URI
  使用复合编码方式需要的数据类型
  
  内部编码信息
  图 2 显示了分析器如何通过自动检测来确定字符编码方式。注意,文档必须具有正确的字节顺序标记 (BOM) 和/或编码声明。
  
 

  在没有提供外部编码信息的情况下,可以使用自动检测。例如:
  
  XML" 作为文件存储在文件系统中。
  发送方没有提供外部编码信息,并且没有规定编码方式。
  
  内部字符编码信息来源的详细信息
  
  我们已经讨论了将会使用何种编码方式以及如何在运行时指定、传输和确定这种方式,就让我们来探讨字节顺序标记和编码声明,这些是自动检测字符编码方式的工具。
  
  字节顺序标记 (BOM)
  BOM(Unicode 字符 U+FEFF、ZERO WIDTH NO-BREAK SPACE)可以出现在 XML 实体的开始部分。在 XML 中,BOM 不仅用于显示输入文本流的字节顺序,而且可以帮助检测字符编码方式。XML 处理器通常检查输入流的前几个字节,以判断编码方式是否为 UTF-16 或 ASCII,因此 XML 处理器能够读取可能在 XML 标题中提供的编码声明。UTF-16 格式的实体需要具有供自动检测使用的 BOM,因为 UTF-16 编码方式具有两种形式:UTF-16BE(big endian:fe ff)和 UTF-16LE(little endian:ff fe)。虽然没有字节顺序问题,但 UTF-8 格式的实体可以具有 BOM (ef bb bf)。BOM 不是文档的一部分,因此不能从用户代码中读取。通常 XML 处理器在必要时添加或删除 BOM。
  
  编码声明
  编码声明是 XML 标题的参数之一(特别称为 XML 或文本声明)。例如
  
  引入编码声明是为了在缺少外部字符编码信息的情况下进行分析时,为那些不使用 UTF-8 或 UTF-16 编码方式的实体提供字符编码信息。一种常见的误解认为,如果 XML 实体正在使用的不是 UTF-8 或 UTF-16 的编码方式,则它必须具有编码声明。实际上,如果知道编码方式,则编码声明没有用处,这经常是问题之所在。XML 分析器提供了一种指定每个输入实体字符编码方式的方法。如果分析器知道实体的编码方式,则声明中的值并不重要。如果以传递给分析器的参数形式从外部提供了编码信息,则通常忽略该值。在其他情况下,根本不必拥有编码标识符。例如,如果实体以 Java 字符流的形式通过 java.io.Reader 传入,则不会进行编码方式的自动检测,因为输入数据类型规定了字符编码方式。当且仅当分析器不知道编码方式时,分析器读取编码声明,以判断编码方式。
  
  如果 XML 实体被存储为文件形式,则使用 BOM 和编码声明来确定字符编码方式是个好习惯。因为文件系统没有识别所存储文件的字符编码方式的机制。Oracle Files、内容管理 SDK 和 XML DB 信息库属于例外,因为您可以将所存储文件的编码信息作为属性进行维护。
  
  XML 及相关的标准定义,外部编码信息优先权高于嵌入式声明。因此内部声明很可能被忽略。为编码声明赋予较低优先权的主要动机之一是,当发生字符集转换时,编码声明变得不正确。当数据从一个环境传送到另一个环境时,字符集转换通常是不可避免的,并且在每次转换期间保持声明的正确性是不切实际的。
  
  编码声明不表示实体的字符集。它仅仅表示文档字符集的物理编码方式(参见边栏),它始终是由 ISO/IEC 10646 和 Unicode 所定义的通用字符集。因此,无论物理编码方式是什么,文档都可能包含通用字符集的任何有效字符。
  
  例如,以上所示的带有前言的文档可能仍包含 Latin-1(ISO 8859 第 1 页)字符集中不提供的字符,通过使用实体引用或外部实体,它可以使用不同于 Latin-1 中引用实体的编码方式。
  
  图 3 演示了一个 XML 文档,它包含几个具有不同字符编码方式的实体。
  

  在没有外部字符编码信息时能够自动检测字符编码方式,这是编码声明的唯一目的。也就是说,其值的使用者是分析器" — 通常,XML 应用程序 — 不应关注该值。但是,可以提供读取编码声明值的方法,以便能够以原始编码方式对实体进行串行化。文档在处理之后进行串行化时,虽然通常可以在 API 或电子表格上指定任意的输出编码方式来覆盖其行为,但 XSL 处理器通常保留原始的编码方式。
  
  编码声明的有效值在 IANA 字符集注册表中进行定义。但是,建议使用 UTF-8 或 UTF-16,在这种情况下编码声明是不必要的,并且保证所有处理器都支持该编码方式。如果选择使用非 Unicode 的编码方式,您应该确保所有将要使用您的文档的处理器都支持该编码方式。
  
  总结
  推荐的应用操作可以总结如下:
  
  协议、格式或 API 规范经常会规定一种特定的编码方式。
  如果没有规定编码方式,则考虑规定 UTF-8 或 UTF-16。
  从外部指定编码信息,除非无法这样做。
  当使用自动检测时,正确地指定 BOM 和/或编码声明。

原文转自:http://www.ltesting.net