案例研究:用 Unicode 处理数据库信息

Unicode 相关的问题引人注目:代码集、数据存储、排序和往返转换保证。Trillium 支持 Unicode,它是一个由 Trillium Software Systems(Harte-Hanks Data Technologies 的一个部门)开发的“数据库清理”系统。

介绍
本文讨论 Trillium Software System 的数据清理产品 "Trillium" 支持 Unicode 的特殊案例,同时,探讨在用数据库处理软件中使用 Unicode 所涉及的一些常规问题。我们将讨论选择 Unicode 而不是其它选项的原因,以及在与 Unicode 相关的开发过程中出现的问题。

什么是数据清理?
基本来说,数据清理是将数据库精简以除去重复记录,并使剩余部分转换成标准可接收格式的过程。这对维护大型数据库的机构(例如长途电话公司、金融机构或零售商店)极有价值。这些公司经常需要将自己的数据与其它最新获得的数据库合并,或者将不同的数据输入源合并成一个数据库。数据清理的好处包括:使直邮成本最小化、增强测量和跟踪数据的能力、从自由格式文本字段中抽取埋藏的重要信息,以及减少数据存储所需的内存需求。

标准模型是将数据输入到数据清理处理器,通过一系列步骤“清理”数据,然后以期望的格式输出清理过的数据库。

清理过程的第一步是分析并调查所有数据类型,并将它们转换成标准、或“规范”的格式。这意味着查看数据字段并确定显示了哪些形式,哪个形式将是标准的,以及转换哪些不符合标准的数据。(术语规范规范化在数据清理中经常用到。这里,我们将它们定义成将数据标准化成具有相同格式的结果和过程。这些格式可以是编码、数据类型和字符类型。

举一个简单例子,某零售目录公司有包含名称、地址和客户号码的大型数据库。然后,该公司收购了另一家目录公司,并希望合并数据库。清理过程的第一步将把第二个数据库的格式转换成与原始数据库的格式匹配。

原始:

数据库 A 数据库 B
A0005432 2-344-984

转换后:

数据库 A 数据库 B
A0005432 B0004984

数据清理的下一步是检查数据库的字段,以标志记录之间可能匹配的数据。例如,Mary E. Smith、Mary Smith 和 M. E. Smith 都在名称字段中出现,并且地址地段中的数据相同,因此可能是重复记录。

客户号# 名称 地址
A0005432 Mary E. Smith 1313 Mockingbird Place
F0009954 Mary Smith 1313 Mockingbird Place
N0003111 M. E. Smith 1313 Mockingbird Place

最后一步是找到并除去匹配的记录。这需要自动或手工查看标记过的数据,以确定可能的匹配是否确实匹配,并除去重复记录。举例说明,将除去上例中的两个记录,因为它们显然是重复项。

还有一步是评估地址数据,并将它转换成国家邮政局正式批准的格式。这一步将地址数据转换成符合折扣邮费的格式。例如,如果零售目录公司有美国客户名称和地址的数据库,他们可能要将所有地址行转换成与美国邮政服务标准相匹配。在处理过的数据库中,所有街道名称标签(如 "Place" 或 "Avenue")将按美国邮政建议缩写成 "PL" 和 "AVE" 等。

原始格式 邮政服务批准的格式
1313 Mockingbird Place 1313 MOCKINGBIRD PL
2304 Elm Street, apt 51 2304 ELM ST 51
1237 - 184th Av. 1237 184 AVE

Trillium 概述
Trillium 是由 Harte Hanks Data Technologies 的 Trillium Software Systems 部门创建的企业范围的数据清理软件。Trillium 用以下四个可调用引擎并通过以上四步处理:Converter、Parser、Matcher 和 "Geocoder"。这些引擎可以联机或以批处理方式运行。

Trillium 支持包括金融、保险、电信、大型软件厂商和零售连锁商店在内的各种行业。客户包括 American Express、MetLife、AT&T、IBM、Intel 和 JC Penney。

处理的常见类型数据包括名称、头衔、地址、电话号码、产品代码、身份证号码、自由文本等。典型的 Trillium 客户处理包含上百万个记录的庞大数据集。因此,处理过程必须高效、快速和精确。数据通常还来源于多个数据源,这要求 Trillium 接受以各种方式格式化和编码的数据。

Trillium ,一种独立于平台和可伸缩的产品,必须适合很多环境 -- 从运行在 MVS 或 AS/400 的大型系统到 Windows 或 Unix 上的小型数据库。

Trillium 的国际化需求
Trillium 已经“国际化”了几年,支持美国、加拿大、欧洲、南美、墨西哥、澳大利亚、马来西亚和其它使用基于拉丁文字的国家。支持这些国家数据的策略与北美模型没有多大不同。

但是,当日本有直接机会,并且中国香港、中国台湾、中国大陆和韩国可能有长期机会时,Trilliumw 认识到“完全国际化”的时机已到来,即在应用程序中支持所有国家语言,同时保留 Trillium 的最佳特性:平台无关性、高效和快速的数据处理以及灵活和可配置的处理。

实现

方法
Trillium Software 选择 Unicode 作为其“完全国际化”模型编码的内部编码模式。为了满足目前和计划中来自全球各国家的需求,需要对大量字符集提供支持。可能的实施方案是使用 MBCS(多字节字符集)抽象,其中,每个字符集可能需要它自己版本的源代码和有条件的运行时代码路径,以及允许对所有语言使用一个代码和运行时代码路径的 Unicode。Unicode 可以通过一种编码为所有语言提供设计简单性和可维护性,而不用支持多个源码或运行时代码路径,以基于语言环境管理不同编码,这使 Unicode 成为更好的选择。

在这种新模型中,Trillium 内部处理通过 UCS2 执行。因为很少有客户用 UCS2 编码数据库,所以需要有一个系统将数据转换到传统字符编码和从传统字符编码转换,以及转换到 UCS2 和从 UCS2 转换。Trillium 选择 Basis Technology 的 Rosette 这种 Unicode 开发平台来执行这种任务。Rosette 允许程序员快速向产品中添加 Unicode 2.1 兼容性。Rosette 丰富的 C 类和与 ISO-C 兼容的众多字符运行时库,支持在 Unicode 和众多广泛使用且特定于语言的传统编码之间灵活转换。

有了这些转换例程,Trillium 可以接受世界上任何数量的字符编码模式,在内部使用 Unicode 处理数据,并以原始编码或任何其它期望的编码返回数据。

这种模型特别适合 Trillium。Trillium 的一个关键组件是 "DDL"(数据定义语言)层,它可以使应用程序对以各种方式表示的数据文件字段进行统一访问。Trillium 描述字段属性的语言已用于各种数值、日期和货币格式,以及 EBCDIC 或 ASCII 编码。将 DDL 扩展成包含所有 Rosette 支持的编码,以及将 DDL 层的职责扩展成表示到数据清理应用程序的一致 Unicode 接口,是 Trillium 体系结构自然而强大的结果。

问题
虽然 Trillium 正计划发展国际市场,但他们目前的需要来自日本,因此,国际产品的开发围绕日语案例进行。以下是在国际产品开发过程中出现的与 Unicode 有关的问题,但大多数例子指的是日语。这里讨论的一般问题对很多类型的数据库应用程序通用。

数据输入/输出

传统编码
传统编码是在 Unicode 出现之前正在使用的任何字符编码标准。传统编码包括国家、国际和厂商编码标准。

日语有几种常见传统编码模式,如 Shift-JIS、EUC-JP、Code Page 932(有少许重要不同的 Shift-JIS 的 Microsoft 衍生)和 ISO2022 编码的 JIS。Trillium 将清理以这些模式中的任何一个编码的数据库。另外,既然 Trillium 处理原来存储在大型机中的公司数据,所以还要考虑日本公司字符集编码,如 IBM-Kanji、NEC-Kanji、KEIS(日立)和 JEF(富士通)。

更多了解日语字符编码的极好参考大全是 Ken Lunde 所著、最新出版的(而且期待很久的)书 CJKV (Chinese, Japanese, Korean and Vietnamese) Information Processing (ISBN 1-56592-224-7)。

传统编码和 UCS2 之间来/自转换
要想使 Trillium 处理 UCS2 数据库,必须将数据库从原始编码转换到 UCS2,并在输出时执行相反转换,如上图所示。有几个代码库可以提供众多传统数据编码和 Unicode 之间的转换。Trillium 选择了 Rosette 库,因为它速度快,“足迹”(程序操作时需要的内存空间)小,以及可获得编码转换之外的标准特性和定制特性。

Rosette 库有可能使任何数量的传统编码(包括上面所列),与 UCS2(或任何通过 UCS2 支持的编码)互相转换。这个库利用优化的代码点映射表来将一种编码精确转换成另一种。

创建映射表
为了将一种传统编码转换成 Unicode,必须定义 Unicode 中的代码点和所有字符共有的传统编码之间的映射。为了此目的,Unicode 联盟提供了数百个映射表,来将传统字符编码中的代码点映射成 Unicode。以下是 Shift-JIS 到 Unicode 映射表的一小部分:

Shift-JIS
代码点(十六进制)
Unicode
代码点(十六进制)
Unicode 名称
(# 是注释标记)
0x82A9 0x304B # HIRAGANA LETTER KA
0x82AA 0x304C # HIRAGANA LETTER GA
0x82AB 0x304D # HIRAGANA LETTER KI
0x82AC 0x304E # HIRAGANA LETTER GI
0x82AD 0x304F # HIRAGANA LETTER KU
0x82AE 0x3050 # HIRAGANA LETTER GU

但是,Unicode 联盟提供的映射集中没有表示所有传统编码。在 Trillium 项目中,需要几种公司编码和 Unicode 之间的转换 -- 这些编码有 EBCDIK(包含日语 Kana 字符的 EBCDIC)、JEF(富士通编码)、KEIS(日立)、LETS-J (UNISYS)、IBM Kanji 和 NEC-Kanji。Unicode 2.0 中没有提供这些映射,也不能轻易从其它来源获得。在这种情况下,需要自己创建映射表。

要创建映射表,首先要研究每一种编码,并调查任何从它继承的“后续”编码:有时候,公司字符集将有多个版本,每个版本略有不同。例如,EBCDIK 编码有几个衍生版本,它们略有不同,例如 IBM 的 CCSID 290 和 CCSID 1027 -- 二者都叫作 "EBCDIK"。重要的是:文件编码的 EBCDIK 版本要与映射表相同,否则数据可能会丢失或毁坏。

在我们的研究中,试图找到定义编码标准的实际代码表。代码表列出编码中的每个字符,并显示它的分配值。找到旧的、不常用和非常专用的标准是困难的。如果没有代码表,就查找编码的描述,描述可能会略述不同的编码范围,并列出编码可能基于的其它厂商、国家或国际标准。例如,很多日语编码都在某些方面基于国家标准 JIS X 0208。

Lunde 的 CJKV Processing 是有关所有大型机和不常见编码信息的极好资源,它包含很多映射表(不一定是与 Unicode 之间的转换)。

当无法找到映射表时,创建映射表的一种方法是使用已知相关的其它表的一部分,然后手工做修改,以满足新映射表所需。例如,除了一些不同之外,NEC-Kanji 代码表直接从 JIS X 0208 生成。因此,使用了 JIS X 0208 到 Unicode 映射表作为 NEC-Kanji 表的基础,然后手工输入不同之处。

另一种定义映射表的方法是通过已经在链中存在的表映射来定义新映射。如果现有表将所需编码映射到任何其它编码,而其它编码有到 Unicode 的映射,则可以合并这两个表以生成新表。例如,在 CJKV Processing 中找到 EBCDIK 到 ASCII 的表,就可以通过合并这两个表定义的映射来生成 EBCDIK 到 Unicode 表。

测试表的最后一步是确认数据成功完成了“往返”转换。通过一个包含编码中所有字符的文件的帮助,很容易完成这一步。该文件可以转换到 Unicode,然后再转换回来。如果有任何不同点,则表不正确。

往返转换(“可转换性”)
Unicode 2.0 发行版中提供的 Unicode 映射表目的在于保证往返转换的正确性,大多数表都没有错误。由于有容易出错的字符对(例如 MACRON (U 00AF) 和 OVERLINE (U 203E)),确实有不明确和有问题的映射出现。只有少数用户才能遇到这些错误,但是这些错误值得注意。下面提供了存在这种问题的两个例子。

一个问题是在传统编码中的两个字符转换到 Unicode 中的同一个字符时出现的。这在转回传统编码时特别会出现问题,因为会产生二义性。

另一个问题在当一个编码正好是另一个的子集时出现,这样与期望会有一些不同。例如,Shift-JIS 表面上正好是 Microsoft Windows Code Page 932 的子集,并且是创建 CP932 的基础。但是,有一些映射点却不符合,如下所示:

Shift-JIS Unicode
0x817C 0x2212 # MINUS SIGN
CP 932 Unicode
0x817C 0xFF0D # FULLWIDTH HYPHEN-MINUS

虽然有疑问的字符可以在往返转换中正确转换成同一个传统编码,但是如果在输出时将 Unicode 文件转换成相关编码,将产生不匹配字符。

下面即将讲到处理“正式”映射表中明显不一致的两种选项。可以改变映射表的本地副本以生成期望的结果。如果采用此策略,最好还要准备使用正式表,因为共享转换文件的其它进程可能会使用其它映射,从而导致文件和数据库中不一致。

当然,如果可以确定只有少数用户能遇到这些问题,也可以接受、忽略错误和“带错误”运行。

无论使用哪个策略,如果无法在 Unicode 标准手册中找到宣称的问题,建议您与 Unicode 联盟 ([email protected]) 联系,以获得讨论和可能的报告。虽然映射表中明确定义了很多不一致,但经常它们由厂商出于正当理由建立,因此不可更改。

存储
因为处理数据库的应用程序要处理大量数据,所以最好使数据长度最小。虽然 16 位 Unicode 可能不总是存储数据的最佳节省空间选择,但如果数据可能以任何世界语言表示,那么它还是处理程序内部数据的最可靠技术。

UCS2 为每个字符保留单一“宽度”或“大小”(16 位)。很多传统编码,特别是日语传统编码,都是可变宽度,一些字符占 8 位,一些占 16 位或更多。例如,Shift-JIS 这种常见的日语编码标准包含 ASCII/JIS 罗马字符(8 位),半宽片假名字符(8 位)以及罗马字符和符号(16 位)。因此,假定有一个 200 个字符的文件 -- 100 个罗马 "ASCII" 字符和 100 个日语字符 -- 如果用 Shift-JIS 编码,则长度将比 Unicode 编码小 25%:

编码 16 位字符 8 位字符 总位数
UCS2 200 0 3200
Shift JIS 100 100 2400

由于文件长度可能会增加,所以通常不希望以 UCS2 存储数据。但是,如果数据库当前包含(或将要包含)来自多个字符集的字符,尽管长度可能会增加,还是强烈建议使用 Unicode。

以 Trillium 为例,因为只在内部处理中使用 UCS2,而数据以原始编码返回,所以只影响到运行 Trillium 所需的临时数据存储空间。当然,除非最终用户在数据文件中选择使用 Unicode,永久存储空间不受影响。

UCS2 对比 UTF8
除了单一宽度 UCS2,Unicode 还支持可变宽格式 -- “UCS 变换格式 (UTF)”。这些格式设计成允许 Unicode 在未经修改的现有 8 位环境中(即 Internet 和文件系统)可靠传送,并更高效地将 ASCII 数据打包。UTF8 是最常见的 UTF,它是将字符分成 8 位“块”,每个字符有一到三块(8-24 位)的方案。"ASCII" 字符占 8 位,日语字符占 24 位,所以在上例中,UCS2 和 UTF8 将占相等空间,但实际并不总是这样:

编码 24 位字符 16 位字符 8 位字符 总位数
UTF8 100 0 100 3200
UCS2 - 200 0 3200
Shift JIS - 100 100 2400

虽然使用 UTF8 作为数据库的编码可能会根据主要字符类型而节省空间,但是以 UTF8 处理字符比 UCS2 更麻烦。因为 UTF8 是变宽编码,使编程变成变得困难和复杂,因为即使是最基本的字符处理函数也要分别检查每一字节,以分辨字符边界。这就降低了处理速度,并需要复杂、易错的代码。UTF8 提供了存储文件的空间优势和在因特网上发送数据的时间优势,但是特定应用的字符操作越复杂,UCS2 提供的优点也越多。

实际文件存储时最适合的数据编码选择取决于存储的数据类型。如果要存储的全部数据是 ASCII 或基于拉丁文的文本,则 ASCII 或 ISO-8859n 效率最高。如果所有数据是日文,则 Shift-JIS 提供最好的数据包装。但是如果数据表示多种语言的文本,则需要 Unicode 的一些衍生。非拉丁符号(其字符在 UTF8 中需要 3 字节表示)越多,UCS2 越比 UTF8 具有空间优势。

定长数据库子段
因为 UCS2 中的字符比 ASCII 或 ISO-8859n 需要更多空间,那些决定在数据库字段中输出 UCS2 的 Trillium 用户可能要将他们的数据库重新格式化为具有更大的字段(更多字节),以维持相同数量的字符。(当然,更改数据字段的编码是客户才有的选项,因为他们的文件编码不受其它应用程序的约束)。这是 Trillium 中的简单操作,因为文件格式转化是其最常用功能之一。

国际编码的使用在数据库格式的选择上带来一个新问题,即不允许存储部分(不完整)多字节字符。例如,定义成包含 UCS2 的字段不应该定义成包含奇数个字节,因为每个 UCS2 需要两字节。如果在存储到数据字段时,没有由实际的编码转换模块 (Rosette) 仔细管理,就可能会只存储半个(或其它部分)字符。如果不加思索地按字节来应用传统的“固定子段长度”数据处理中的字段截断,则将产生损坏的无效字符。

要解决 Trillium 或其它应用程序中的这种问题,其中,固定缓冲区可能需要截断多字节代码中字符串,我们向 Rosette 添加了一些特性(“字符下沉类”),如果缓冲区要从编码转换接受输出,这些功能只发送完整的字符表示,并向客户指明执行的操作(截断或未截断)和实际填充的字节数量。

整序/排序
直到最近,Unicode 标准还没有提供任何“文化适用”或“特定于语言环境”的整序或排序。因此,在开发 Trillium 时,不得不创建整序例程(可用做排序例程所用的比较谓词)。Unicode 手册中有对国际文本排序的指南,1998 年 12 月,Unicode 联盟发布了解决语言环境排序问题的草案技术报告 (#10)。但是,建议对日语剪裁 TR10 中的整序算法。

日语排序
TR10 中概述的算法使用 Unicode 十六进制数字作为汉字字符(在日语中也称为 "Kanji"*)整序的基础。Unicode 顺序通过基本笔划排序汉字字符。虽然认为这是整序汉语的可接受顺序,但是建议使用其它日语排序方法,这需要对 TR10 做一些剪裁。

真正的日语排序是困难的,因为需要“读”字(或发音)知识,对于以表意符号书写的字,这需要(这是幸运的)大量查阅字典。最糟糕的是,以专有名称为例,没有外部提示就无法进行。因为这种排序难以实现,所以,日语计算机用户已逐渐接受称为“JIS 排序”的方法,一种基于 JIS 标准中分配给字符的代码点值的排序方法。

Unicode 开发平台(如 Rosette)提供比较例程来进行语言环境的排序。为了支持 Trillium,我们向 Rosette 添加了一个例程,以 JIS 整序次序比较两个 Unicode 字符串,可以将它用于与标准 C/C 排序例程(如 qsort)一起使用的谓词。该 Rosette 谓词将数据转换成 EUC-JP,然后做二进制比较。我们选择 EUC-JP 而不是 Shift-JIS 或其它基于 JIS 的标准 ,是因为它包含 JIS-X-0201、JIS-X-0208 和 JIS-X-0212,因此尽可能多地包含了日语字符集。

Unicode 兼容性字符

在排序或搜索 Unicode 中的字符之前,通常建议将所有“兼容性字符”转换成它们的标准形式。兼容性字符是只在 Unicode 中存在的字符,用于支持在传统字符集中出现的字符变体。兼容性字符总有一个被认为是“标准”的类似表示。

例如,在 Shift-JIS 中,有一些片假名字符以“半宽”表示,还有一些以“全宽”表示,后者是标准形式,在 Unicode 中也认为是标准形式。但是,因为半宽表示在 Shift-JIS 中存在,而且 Unicode 保证与传统编码之间的往返转换,这些半宽字符也在 Unicode 中表示。

应该将兼容性字符转换成它们的标准形式,以便相似字符可以正确排序。例如,以下 UCS2 元素表示相同字符,但字型不同,排序时应该将它们组在一起:

KATAKANA LETTER KA U 30AB
HALFWIDTH KATAKANA LETTER KA U FF76

如果不规范兼容性字符,以半宽表示的数据在排序时将与以全宽表示的数据分开。在上例中,应该先将半宽 "ka" 转换成全宽等价字符,然后再比较。

TR10 中提供的“缺省 Unicode 整序元素表”正确处理了非标准日语字符表示。

结束语
使用 Unicode 作为 Trillium 数据库处理引擎的编码是自然选择,因为应用程序需要支持世界上许多字符集中的字符,可以想象,即使一个数据文件也是如此。Unicode 开发平台 Rosette 支持在输入和输出时转换编码的模型,它提供 Unicode 和大量传统编码之间互相转换的例程。将大量编码与一个标准内部编码互相转换的模型,自然地适合 Trillium 并作为其现有数据访问体系结构的扩展。

除了在开发过程中出现的问题之外,UCS2 也在以下几方面证明自己优于其它解决方案:提供出众的数据处理以及使编程更容易和可靠的定宽字符、处理所有现代字符集的能力、添加如整序/排序特性的灵活框架。简而言之,我们发现:Unicode 实现了其设计预想,并为不断变小的全球市场的信息处理提供了一种出色的解决方案。

你可能感兴趣的:(数据库,存储,语言,processing,microsoft,ibm)