• 沒有找到結果。

字符集基础

在文檔中 Ron Hitchens 著 裴小星 译 (頁 191-0)

第六章 字符集

6.1 字符集基础

在讨论 java.nio.charsets 中新类的细节之前,让我们先来定义一些与字符集和 字符代码转换相关的术语。新的字符集类表示到该领域的更标准化的方法,所以明确术语 的使用是很重要的。

Character set(字符集)

字符的集合,也就是,带有特殊语义的符号。字母“A”是一个字符。“%”也 是一个字符。没有内在数字价值,与 ASCII,Unicode,甚至是电脑也没有任何的 直接联系。在电脑产生前的很长一段时间内,符号就已经存在了。

Coded character set(编码字符集)

一个数值赋给一个字符的集合。把代码赋值给字符,这样它们就可以用特定的字 符编码集表达数字的结果。其他的编码字符集可以赋不同的数值到同一个字符 上。字符集映射通常是由标准组织确定的,例如 USASCII,ISO 8859-1,Unicode (ISO 10646-1),以及 JIS X0201。

Character-encoding scheme(字符编码方案)

编码字符集成员到八位字节(8 bit 字节)的映射。编码方案定义了如何把字符编 码的序列表达为字节序列。字符编码的数值不需要与编码字节相同,也不需要是 一对一或一对多个的关系。原则上,把字符集编码和解码近似视为对象的序列化 和反序列化。

通常字符数据编码是用于网络传输或文件存储。编码方案不是字符集,它是映射;但 1 他不设法阻止地球毁灭,但是这与主题不相关。

2 通过http://babelfish.altavista.com/访问。

188 是因为它们之间的紧密联系,大部分编码都与一个独立的字符集相关联。例如,UTF-8,

仅用来编码 Unicode 字符集。尽管如此,用一个编码方案处理多个字符集还是可能发生 的。例如,EUC 可以对几个亚洲语言的字符进行编码。

图 6-1 是使用 UTF-8 编码方案将 Unicode 字符序列编码为字节序列的图形表达式。UTF-8 把小于 0x80 的字符代码值编码成一个单字节值(标准 ASCII)。所有其他的 Unicode 字 符都被编码成 2 到 6 个字节的多字节序列(http://www.ietf.org/rfc/rfc2279.txt)。

Charset(字符集)

术语 charset 是在 RFC2278(http://ietf.org/rfc/rfc2278.txt)中定义的。它是编码字符集 和字符编码方案的集合。java.nio.charset 包的锚类是 Charset,它封装字符 集抽取。

图 6-1. 字符编码成 UTF-8.

Unicode 是 16-位字符编码。3它试着把全世界所有语言的字符集统一到一个独立的、

全面的映射中。它赢得了一席之地,但是目前仍有许多其他字符编码正在被广泛的使用。

大部分的操作系统在 I/O 与文件存储方面仍是以字节为导向的,所以无论使用何种编码,

Unicode 或其他编码,在字节序列和字符集编码之间仍需要进行转化。

由 java.nio.charset 包组成的类满足了这个需求。这不是 Java 平台第一次处理字 符集编码,但是它是最系统、最全面、以及最灵活的解决方式。

java.nio.charset.spi 包提供服务器供给接口(SPI),使编码器和解码器可以根据 需要选择插入。

6.2 字符集

到 JDK1.4 为止,每个 JVM 实现都需要支持标准字符集,表 6-1 列出了具体内容。

JVM 实现可以随意的支持额外的字符集,但是必须提供这个最大集。(以您的 JVM 版本 文件为参考,确定额外的字符集是否可用。)注意,尽管所有的 JVM 必须至少支持下列字 符集,但是没有指定默认字符集,也没有要求是这些标准字符集之一。在 JVM 启动时确定 默认值,取决于潜在的操作系统环境、区域设置、和/或 JVM 配置。如果您需要一个指定 的字符集,最安全的办法是明确的命名它。不要假设默认部署与您的开发环境相同。

3 或者看起来是这样的。Unicode 现在可以定义大于 16 位的字符编码。直到 1.5 版本,Jave 才支持这些新 的、扩展的编码。

189 表 6-1. 必要字符集

字符集 名称

描述

US-ASCII 7-位 ASCII,ISO 646-US。Unicode 字符集的基本拉丁模块。这是常见的美式英语字符 集。

ISO-8859-1 ISO-LATIN-1。大部分欧洲语言使用的字符集。这是 US-ASCII 的扩展集并囊括了大部分非 英语的欧洲字符。(见http://www.unicode.org/charts/。)ISO-LATIN-1 的字符在 8 位 内进行编码。

UTF-8 8-位 UCS 转换格式。由 RFC2279 以及 Unicode 标准 3.0(修正版)指定。这是字位导向的 字符编码。小于 0x80 的 ASCII 字符被编码为单字节。其他字符被编码为两个或多个字 节。对于多个序列,用首字节的高序位编码下面字节的数量。(见

http://www.ietf.org/rfc/rfc2279.txt。)UTF-8 与 ASCII 的互操作良好,因为简单的 ASCII 文件就是良好的 UTF-8 编码,而小于 0x80 的字符的 UTF-8 编码就是 ASCII 文件。

UTF-16BE 16-位 UCS 转换格式,高位字序。每个 Unicode 字符都被编码为 2-字节序列,首先编写高 序位 8 位(bit)。

UTF-16LE 16-位 UCS 转换格式,低位字序。每个 Unicode 字符都被编码为 2-字节序列,首先编写低 序位 8 位(bit)。

UTF-16 16-位 UCS 转换格式。字序由可选的字序标记确定。UTF-16 字符在 RFC2781 中指定。UTF-16BE 和 UTF-中指定。UTF-16BE 格式编码成 16-位,因而由字序确定。UTF-16 是可移植编码,它使用导 向字节标记来表示编码字节流的其余部分是否是 UTF-16BE 或 UTF-16LE。见表 6-2。

字符集名称不区分大小写,也就是,当比较字符集名称时认为大写字母和小写字母相同。

互联网名称分配机构(IANA)维护所有正式注册的字符集名称,而表 6-1 中列出的所有名 称都是在 IANA 注册的标准名称。

UTF-16BE 和 UTF-16LE 把每个字符编码为一个 2-字节数值。因此这类编码的解码器必须 要预先了解数据是如何编码的,或者根据编码数据流本身来确定字节顺序的方式。UTF-16 编码承认一种字节顺序标记:Unicode 字符\uFEFF。只有发生在编码流的开端时字节顺序 标记才表现为其特殊含义。如果之后遇到该值,它是根据其定义的 Unicode 值(零宽度,

无间断空格)被映射。外来的,小字节序系统可能会优先考虑\uFEF 并且把流编码为 UTF-16LE。使用 UTF-16 编码优先考虑和认可字节顺序标记使系统带有不同的内部字节顺 序,从而与 Unicode 数据交流。

190 表 6-2 解释了 Java 平台针对不同的组合所采取的操作。

表 6-2.UTF-16 字符集编码/解码

UTF-16 UTF-16BE UTF-16LE

编码 预先考虑字节标

package com.ronsoft.books.nio.charset;

import java.nio.charset.Charset;

import java.nio.ByteBuffer;

/**

* Charset encoding test. Run the same input string, which contains

* some non-ascii characters, through several Charset encoders and dump out * the hex values of the resulting byte sequences.

*

* @author Ron Hitchens ([email protected]) */

public class EncodeTest {

191 */

private static void doEncode (Charset cs, String input) {

Charset: US-ASCII Input: żMańana?

Charset: ISO-8859-1 Input: żMańana? Charset: UTF-8 Input: żMańana?

192

Charset: UTF-16BE Input: żMańana? Charset: UTF-16LE Input: żMańana?

Charset: UTF-16 Input: żMańana?

193

6.2.1 字符集类

让我们深入到 Charset 类的 API 中(用图 6-2 概括说明):

package java.nio.charset;

public abstract class Charset implements Comparable {

public static boolean isSupported (String charsetName) public static Charset forName (String charsetName) public static SortedMap availableCharsets( ) public final String name( )

public final Set aliases( ) public String displayName( )

public String displayName (Locale locale) public final boolean isRegistered( ) public boolean canEncode( )

public abstract CharsetEncoder newEncoder( );

public final ByteBuffer encode (CharBuffer cb) public final ByteBuffer encode (String str) public abstract CharsetDecoder newDecoder( );

public final CharBuffer decode (ByteBuffer bb) public abstract boolean contains (Charset cs);

public final boolean equals (Object ob) public final int compareTo (Object ob) public final int hashCode( )

public final String toString( ) }

194 图 6-2. 字符集类

Charset 类封装特定字符集的永恒信息。Charset 是抽取。通过调用静态工厂方法 forName()获得具体实例,导入所需字符集的名称。所有的 Charset 方法都是线程安全的;

单一实例可以在多个线程中共享。

可以调用布尔类(boolean class)方法 isSupported()来确定在 JVM 运行中当前指定的 字符集是否可用。通过 Charset SPI 机制可以动态安装新的字符集,所以给定字符集名称的 答案可以随时间变化。在第 6.3 节中论述了 Charset SPI。

一个字符集可以有多个名称。通常它有一个规范名称但是也有零个或多个别名。规范 名称或别名都可以通过 forName()和 isSupported()进行使用。

一些字符集也有历史遗留的名称,它们用于之前的 Java 平台版本并且向后兼容。字符

195 集的历史名称是由 InputStreamReader 和 OutputStream-Writer 类的 getEncoding()返回。如果 字符集有历史名称,那么它将是规范名称或者 Charset 的别名之一。Charset 类不提供指出 历史名称的标示。

静态类方法的最后一个,availableCharsets(),将返回在 JVM 中当前有效的所有字符集 的 java.util.SortedMap。正如 isSupported(),如果安装新的字符集返回的值会随着时间改 变。返回映射的成员将是用它们的规范名称作为密钥的 Charset 对象。迭代时,映射将根 据规范名称按字母顺序排列。

availableCharsets()方法可能很慢。虽然支持许多字符集,但是通常它们只在有明确要 求时才能被创建。调用 availableCharsets()需要实例化所有已知的 Charset 对象。实例化 Charset 可能需要加载库、网络资源访问、翻译表计算等等。如果您知道要使用的字符集的 名称,使用 forName()方法。当您需要列举所有可用的字符集时使用 availableCharsets()—

例如,面向交互用户展示一个选项。假设在此期间未安装新的字符集,通过 availableCharsets()返回的 Map 精确的包含了用 forName()可返回的相同的字符集。

一旦获取 Charset 实例的参数,name()方法将返回字符集的规范名称,并且 aliases()将 给出包含别名的 Set。由 aliases()返回的 Set 将永远不会是 null,但是可以为空。

每个 Charset 对象也有两个 displayName()方法。这些方法的默认实现仅仅返回规范字 符集名称。这些方法可以提供本地化的显示名称,例如,用在菜单或选项中。

displayName()方法可以使用 Locale 参数指定一个地方化的环境。无参数版本使用默认区域 设置。

如本节开端提到的,IANA 是维护字符集名称的权威登记机构。如果给出的 Charset 对 象表示在 IANA 注册的字符集,那么 isRegistered()方法将返回 true。如果是这样的话,那 么 Charset 对象需要满足几个条件:

 字符集的规范名称应与在 IANA 注册的名称相符。

 如果 IANA 用同一个字符集注册了多个名称,对象返回的规范名称应该与 IANA 注册 中的 MIME-首选名称相符。

 如果字符集名称从注册中移除,那么当前的规范名称应保留为别名。

 如果字符集没有在 IANA 注册,它的规范名称必须以“X-”或“x-”开头。

大多数情况下,只有 JVM 卖家才会关注这些规则。然而,如果您打算以您自己的字符 集作为应用的一部分,那么了解这些不该做的事情将对您很有帮助。针对 isRegistered()您 应该返回 false 并以“X-”开头命名您的字符集。见 6.3 节。

196

6.2.2 字符集比较

下面的代码中包含了我们将在本节中详述的 Charset 的 API 方法:

public abstract class Charset implements Comparable {

// This is a partial API listing

public abstract boolean contains (Charset cs);

public final boolean equals (Object ob) public final int compareTo (Object ob) public final int hashCode( )

public final String toString( ) }

回想一下,字符集是由字符的编码集与该字符集的编码方案组成的。与普通的集合类

回想一下,字符集是由字符的编码集与该字符集的编码方案组成的。与普通的集合类

在文檔中 Ron Hitchens 著 裴小星 译 (頁 191-0)

相關文件