EBCDIC 与 GBK 的字符编码及其转换(转)

来源:互联网 发布:免费wifi软件 编辑:程序博客网 时间:2024/04/29 03:05

概览

有些用户在使用 AIX 时在字符编码方面遇到一些困惑,请看下面的场景:

1,用户用从 AIX 利用 FTP 客户端登录上 IBM i,切换到某个 Library/File,然后 get 其中的某个 Member 到本地机器,用文本编辑工具打开时发现是乱码,和自己在 IBM i 上看到的完全不一样;

2,用户在实现 AIX 平台与 z/OS 平台通信的 TCP/IP 应用程序中,会遇到传送的报文内容并没有按照预想的出现。

众所周知,AIX 平台的字符编码是基于 ASCII 的,但在与非 ASCII 字符编码体系的平台通信时,就会涉及到编码转换的问题,比如 AIX 平台与 IBM i,z/OS 平台通信。由于 IBM i 和 z/OS 历史悠久,在系统的字符编码以及多国语言支持方面有其鲜明的特点,衍生出其独有的概念与体系。本文通过介绍字符编码的概念及其体系,帮助用户解决一些类似上述的问题。

字符集与字符编码

在我们的计算机世界里,字符是十分基本的元素,了解字符集,字符编码的基本特性,是解决字符识别与转换,实际开发中的字符处理(如读取,截取)等问题的基础与关键。

字符集(Character Set),顾名思义,特定字符的集合。字符集并没定义字符的顺序,排序的方法以及其他更多的特性。字符集通常只是定义了字符的名字以及字符形状的外在表现 ;

字符编码(Character Encoding),在定义好的字符集基础上,设计出一种方法 ( 或者算法 ), 将字符集的字符与二进制做一个映射,使得计算机能够识别和存储。

由此看出,字符集和字符编码是紧密相联的。计算机会在字符集的基础上建立相应的字符图形。对于某个文件,在计算机读取时,会按照实现约定好的字符编码来进行读取,然后根据对应的字符转为字符图形,最后呈现在用户面前的才是熟悉的字符。

下面先介绍与本文相关的几种字符编码方案:

ASCII(American Standard Code for Information Interchange)

在 20 世纪 60 年代由 ANSI 组织制定的标准的单字节编码方案。ASCII 编码使用 7 位二进制的组合(字节的最高位忽略)来表示 128 个英文字符 , 从而顺利的解决了美国英文的编码问题,由于 ASCII 码出现比较早,后来的很多编码方案都是受它的影响。

扩展 ASCII-ISO8859

由于英文字符总数不多,所以标准的 ASCII 就能很好的解决问题,但对于欧洲其他国家,比如希腊,就有其希腊语的特定需要。因此,为了解决这个问题,国际化标准组织借鉴了标准 ASCII 的设计思想,创造了利用 8 位二进制数来表示字符的扩展 ASCII,并制定了一系列标准 :ISO8859。其原理就是在 0-127 的编码和标准 ASCII 相兼容基础上,将 128 到 255 用作其他语言字符的编码,这样,各种语言就可以制定自己的扩展 ASCII 字符 . 这样就得到了大量不同的编码表,比如 ISO8859-1 字符集,也就是 Latin-1,是西欧常用字符,包括德法两国的字母。ISO8859-2 字符集,也称为 Latin-2,收集了东欧字符。AIX 上的编码就是基于 ASCII-ISO8859 标准集的。

GB2321

西方的语言是由字符总数不多的单词组成,所以扩展 ASCII 就能满足需求了,但对于 CJK (Chinese,Japanese,Korean)等亚洲国家来说,256 个字符是远远不够表示自己国家的全部字符。中国专家发挥了聪明才智,借鉴了 ISO8859 的成功经验,利用双字节来表示汉字,为了具有兼容性,每个字节的 0-127 均为 ASCII 保留,低字节使用从 0xA1-0xFE,高字节使用从 0xB0-0xF7 的区间,这样就能表示 94*72 = 6768 个中文汉字了。这个编码标准就是 GB2312-80 (国家标准)。

GBK

汉字的总数可以用浩如烟海来形容,在使用 GB2312 过程中发现有很多汉字还是没有包括在其中 . 因此 1995 年,推出了汉字内码扩展规范,即 GBK( 国标扩展 ),向下兼容 GB2312,向上支持 ISO10646. GBK 也是采用双字节,总体编码在 8140-FEFE 之间,高字节在 0x81-0xFE 之间,低字节在 0x40-0xFE 之间,不包括 7F。在 GBK 1.0 中共收录了 21886 个符号,汉字有 21003 个。GBK 虽然不是国家标准,只是一个规范,但是却得到了非常广泛的应用,Windows 简体中文版的缺省内码还是 GBK。图 1 为 GBK 的编码结构(Code Scheme)。


图 1.GBK 的编码结构(Code Scheme)
EBCDIC 与 GBK 的字符编码及其转换

其中 GBK1 收录除 GB2312 符号外的增补符号 ,GBK5 为非中文字符集,GBK2 收录 GB2312 汉字,GBK3 收录 CJK 汉字 ,GBK4 收录 CJK 汉字和增补汉字,UDC 区为用户自定义字符区 .

GB18030

GB18030 是最新的汉字编码字符集国家标准,向下兼容 GBK 和 GB2312 标准。GB18030 编码是一二四字节变长编码,收录了中日韩 27484 个汉字。

EBCDIC

EBCDIC--Extended Binary Coded Decimal Interchange Code(扩展二进制编码的十进制交换码),是字母或数字字符的二进制编码,是 IBM 专门为它的 z/OS 和 IBM i(原 AS/400)的操作系统使用的字符编码。

Unicode 不是我们讨论的重点,在这里就不介绍了。

CDRA 字符编码体系

IBM i 和 z/OS 是支持多国语言的平台,用户可以使用任何一种你想使用的语言,当然,前提是你的机器安装了这种语言。下面,简单介绍其基本原理。

CDRA--Character Data Representation Architecture(字符数据表示架构),这个 IBM 架构定义了一整套标识,资源,约定以及 API 来实现在数据处理环境中图形字符数据的一致性表示,处理,交换和在数据转换时维护数据完整性。可以发现,CDRA 涵盖范围主要是四方面:一个标识或标签系统,来保证图形字符数据的有效和唯一的呈现;一套可移植的 API;一套支持标签和服务的资源;一套使用标签和服务的约定;还有在图形字符转换的策略。我们从其中核心的几方面来认识 CDRA。

CDRA 对字符集的分类 ---Character Set Groups

为了减少正在使用的图形字符集(Graphic Character Set)和代码页(Code Page)的不断增加带来的问题,IBM 将不同国家使用的不同语言,根据语言特有的特性进行分组。如 Figure 2 所示:

图 2. 字符集的分类
EBCDIC 与 GBK 的字符编码及其转换

Group1 包括使用单字节编码(SBCS)的 Latin Alphabet Number 1 字符集的语言。

Group1a 同样是以单字节编码,包含非拉丁字母以及没在 Latin Alphabet Number 1 的拉丁字母。

Group2 包含了远东地区 ( 中国,日本,韩国,泰国等 ) 的语言。这些国家和地区的图形字符是使用多字节编码 (DBCS 或 MBCS) 的。

Group Universal 包括了所有支持 Unicode 和 ISO-10646 的所有大字符集。

CDRA 的命名系统 ---CDRA Identifiers

首先要明确的是 CDRA 要处理的对象是图形字符数据(Graphic Character Data)以及某些控制字符数据,其中介绍比较关键的几个 ID:

Graphic Character Global Identifier and Graphic Character UCS Identifier(GCGID,GCUID)

主要用于为 Character Set 和 Code Page 呈现图形字符所用,是与编码独立的,并不直接参与 Tag Data。GCGID 在呈现字符处理方面,比如格式化,表现和打印等,具有极其重要的作用。

Encoding Scheme Identifier(ESID),这就是我们非常熟悉的字符编码方案。CDRA 主要的 Encoding Scheme 如表 1:

表 1. CDRA 主要的 Encoding Scheme

ESIDInterpretation1100EBCDIC, single-byte1200EBCDIC, double-byte2100IBM-PC Data, single-byte2200IBM-PC Data, double-byte3100IBM-PC Display, single-byte3200IBM-PC Display, double-byte4100ISO 8, single-byte

Code Page Global Identifier(CPGID)

即平常所说的 Code Page,也就是经过编码后,字符代码与二进制的映射表,是 Code Points 的集合。

Coded Character Set Identifier(CCSID)

Encoding Scheme ID,Coded Graphic Character Set Global ID 以及 Code Page ID 等组成了 Long-Form ID,以此能清楚地描述 CDRA 的 ID 体系 , 但很多实现和架构并不支持变长的 Tags, 为此,CDRA 提出了 CCSID 的解决方案。一个 16 位的 CCSID 代表着一种数据编码,给出一个 CCSID 和 Code Point,就能唯一的定位到一个字符。表 2 能看出一些常见的 CCSID 以及其他 ID 的映射关系。

表 2. 常见的 CCSID 与其他 ID 的关系

CCSIDCode PageCharacter SetEncodingDescription0003700037006971100US, Canada, Netherlands, Portugal, Brazil0042300423002181100Greece0081900819006974100ISO 8859-1; Latin Alphabet NO. 10085000850011062100PC Data; MLP 222 Latin Alphabet 10093500836 0083701174 009371301Simple Chinese(Extended Range)0138601114 0138565535 655352300Simplified Chinese PC Data GBK mixed

由上表可以发现,CCSID 和 Code Page ID 保持很好的同一性,这是因为现有的字符架构和实现也有了 Tag 的概念,有些是用 Code Page ID,而有些则是 Code Page ID 和 Character Set ID 一起,为了使 CDRA 具有更好的兼容性和可理解性,CDRA 会尽可能的将 CCSID 与其 Code Page 的值设为一样。

通过 CCSID,我们可以知道它是如何进行编码的,它都包含了哪些字符,以及这些字符的图形是如何呈现的,这样这些 ID 就构成了浑然一体的 CDRA 命名系统。

如何保证 Graphic Character Integrity--Tag CCSID

国际化必然带来数据完整性的挑战,如果本国用户不能准确与国外用户进行正常的交流,国际化是无从谈起的。通过 Tagging CCSID,我们就可以维护数据的完整性(Data Integrity)。所谓 Tagging,这是用来识别与呈现字符数据的主要手段,在给一个对象(FILE,Job,Database Table 或是 Database Stream)的 Field 添加这样的标识,就叫 Tagging。譬如,在 IBM i,当我们将一个 Physical File tag 为 CCSID 037,那么在 United Kingdom(job CCSID 285)和 Denmark(job CCSID 277)的用户则可以看到同样的文件内容,如表 3。这种转换和映射是由数据库来完成的。

表 3. 同一字符在不同 CCSID 下的区别

CountryKeyboard TypeCode PageCCSIDCode PointCharacterU.SUSB03700037X5B$U.KUKB28500285X4A$DenmarkDMB27700277X67$

CCSID 显得特别的重要,因为支持 CDRA 的平台需要在不同语言的主机之间交换数据。不同语言,键盘和终端显示可以安装在同一平台上,这些都是 CCSID 在提供强大的支持。

IBM i 的 DBCS 支持

在第一章提到,有些国家与地区的语言比较复杂,一个字节不能将其所有语言文件进行正确的表达,所以必须用两个或更多的字节来进行编码,比如 GB2312 和 GBK 等。IBM i 对 DBCS 提供了很好的支持。

Code Schemes

IBM i 支持以下 4 种 DBCS:

IBM Japanese Character Set;

IBM Korean Character Set;

IBM Simplified Chinese Character Set;

IBM Traditional Chinese Character Set。

如图 3 是 CCSID935 中 DBCS 的 Code Scheme。

图 3.CCSID 935 中 DBCS 的 Code Scheme
EBCDIC 与 GBK 的字符编码及其转换

其中 Area 1 包括 GBK/1 和部分 GBK/5 的字符;Area 2 为全部 GBK/2 的字符;Area 3 为全部 GBK/3 的字符;Area 4 为 GBK/4a 中全部 ISO 10646-1 的 CJK 字符;Area 4b 为其他 GBK/4 的字符;Area 5 为部分 GBK/5 字符;Area 6 为用户自定义字符。

DBCS 与 SBCS 的区分

如果 IBM-host 的 code scheme(比如 EBCDIC)正在使用,那系统会以 shift-control 字符作为 double-byte 字符的起始和终结的标识。其中 Shift-out(SO)字符,十六进制为 0E 是起始符,Shift-in(SI)字符,十六进制为 0F 是终结符。请看一下 CCSID 为 935 的例子,如图 4:

图 4.DBCS 与 SBCS 的区分
EBCDIC 与 GBK 的字符编码及其转换

如果要在 CL 命令中输入双字节字符 , 可以按照一下步骤 :

先输入单引号 (');

输入一个 shift-out 字符 ;

输入你所需的双字节字符 ;

再输入一个 shift-in 字符 ;

最后以一个单引号结束 (');

常见的简体中文字符集

如表 4 是 IBM i 支持的常见的简体中文字符集。值得注意的是,在 AIX 上 GBK 的字符集,在 IBM i 对应的是 01388。这个 CCSID 是在 IBM i 上最常用的简体中文编码之一。

表 4. 常见的简体中文字符集

CCSIDEncoding SchemeDescription008361100Simplified Chinese (extended range)008371200Simplified Chinese009351301Simplified Chinese (extended range)013812300Simplified Chinese GB personal computer mixed SBCS and DBCS013862300Simplified Chinese PC Data GBK mixed,all GBK character set and others013881301Simplified Chinese DBCS- GB 18030 Host with UDCs and Uygur extension.

AIX 上的字符转换

AIX 的字符转换是由系统内部自动完成的,就如同上述的一个例子。AIX 与 IBM i 进行通信时,那内部到底发生了些什么?

图 5. 字符转换的过程
EBCDIC 与 GBK 的字符编码及其转换

如图 5 所示即为字符转换的过程 , 转换表 (Conversion Table) 是已经设计好了,但单单有转换表还不能保证在不同的计算环境中数据传输和共享的准确 . 而转换方法就是要准确的使用转换表,以确保输出是期望的结果 . 转换方法会分析输入数据的 Encoding Scheme 和 String Type,进行必要处理后,然后根据要求输出数据的 Encoding Scheme 和 String Type,再选择合适的转换表进行转换,才能得到准确的转换结果。对输入数据进行的处理包括子字符串的切割,如果输入数据包含 code extension 的控制字符。每一类子字符串应该具有相同的 Code Page 和 Character Set。

在 AIX 上的目录:/usr/lib/nls/loc/iconvTable 下可以看到一部分转换表。

要注意的是,并不是所有图形字符都彼此支持相互转换的 . 显然,CCSID935 中的 DBCS 是无法转换为 CCSID037 的 SBCS. 这也就是为什么将 CCSID 为 935 的文件转换为 CCSID 为 037 后,里面的中文字符全部是不可阅读的 .

如何通过编程实现自定义的字符转换

上述均是字符数据处理的一些基本原理,在实际产品开发中会经常需要对字符进行转换,比如碰到一些文件打开后是乱码;再比如在网络通信应用程序中,为了减轻服务器端对编码转换的负担,客户端可以先将码流转换为服务器端的编码,再进行网络传输(比如 AIX 平台和 IBM i 的通信),下面介绍两种字符转换方法,以供参考。

利用 iconv comman

iconv 命令应该对很多人来说都很熟悉,它可以将字符从一种字符编码转换为另外一种字符编码,而且非常简单易用,比如将文件从 GBK 转换为 CCSID935:

 iconv -f GBK -t IBM_935 file.txt 

而 iconv --list 可以查看 iconv 支持的字符编码方案。

d

利用 API

iconv API 使用也是非常方便的,如下代码示例,将输入字符串从 CCSID 为 850 转换为 037:


清单 1. 将字符串从 CCSID 为 850 转换为 037

 int EbcdicToAscii(char *sIn, char *sOut) 
 { 
  iconv_t cd; 
  size_t inBytesLeft = 0; 
  size_t outBytesLeft = 0; 
  char *inTemp = sIn; 
  char *outTemp = sOut; 
  char localCode[20], targetCode[20]; 
 
  inBytesLeft = strlen(sIn); 
  outBytesLeft = strlen(sIn) * 2; 
  memset(&localCode, 0x00, sizeof(localCode)); 
  sprintf(localCode, "%s", "IBM-850");/*850 ASCII */ 
 
  memset(&targetCode, 0x00, sizeof(targetCode)); 
  sprintf(targetCode, "%s", "IBM-037"); /* 037 for EBCDIC */ 
 
  cd = iconv_open(localCode, targetCode); 
   
  if (iconv(cd, &inTemp, &inBytesLeft, &outTemp, &outBytesLeft) != 0) 
  { 
   printf("Error calling iconv\n"); 
   if (iconv_close(cd) == -1) 
   { 
     printf("Error calling iconv_Close\n"); 
     return 1; 
   } 
  } 
 
  if (iconv_close(cd) == -1) 
  { 
    printf("Error calling iconv_Close\n"); 
    return (1); 
  } 
 
  return 0; 
 } 

AIX,IBM i,QSHELL 和 PASE(Portable Application Solution Environment)均提供了 iconv 的支持。下面以 AIX 为例,对 iconv 的基本原理做简单的介绍:当执行 iconv,系统会调用 /usr/lib/nls/loc/iconv 中相应的可执行程序(Converter Programs),根据需要转换的字符编码,在 /usr/lib/nls/loc/iconvTable 中选中相应的转换表(Converter Tables),并完成转换。用户也可以写自己的转换程序,可在编译时与 libiconv.a 链接,那程序就会在 /usr/lib/nls/loc/iconv 目录下生成一个转换程序,而转换表需要利用 genxlt 工具来帮助生成。

关于字符转换的一些小 Tips

在 AIX 上将 CCSID 为 037 的文件转换为 UTF-8,在命令行敲入:iconv -f IBM-037 -t UTF-8 file.txt

AIX 向 IBM i 传送带中文字符的文件:

在 IBM i 创建一个 CCSID 为 935 的 Source Physical File,在 CL 命令行中敲入 : CRTSRCPF FILE(QTEMP/TEMP) CCSID(935)

在 AIX 上 FTP 到 IBM i,然后在命令行敲入:quote type c 1386(GBK) 或者 1381(GB2312),表明现在要传送的文件的字符编码是 GBK 或者 GB2312 类型的。在 IBM i 端如何转换,则由 IBM i 端的 CCSID 决定,这就是为什么第一步需要创建 CCSID 为 935 的文件。IBM i 端会内部将 1386(1381)的编码转换为 935 的编码。然后在命令行敲入 put localfile temp.temp 完成传送。

总结

对字符编码概念,体系以及字符转换原理进行一定的学习和理解,对在涉及不同平台交换数据以及对字符进行处理时比较有帮助,也比较有必要。

0 0
原创粉丝点击