Debian中文环境配置及几种中文编码的探究(From: 天马行空)

Debian中文环境配置及几种中文编码的探究

国际化(Internationalization,简写为I18N)是指软件能用于多国语言环境的能力,它在系统的低层函数库中提供一组标准的函数接口,能根据本地化(locale )设置显示该地区语言环境的信息。本地化(Localization 简写为L10N )是指将本地区的语言环境数据安装在系统底层的数据库中,以便让系统函数存取来显示正确的文字信息。多语言化(Multilingualization 简写为M17N )是指程序可以处理多种语言的过程。本地化和多语言化都是国际化框架中的组成部份。

本地化在Linux 中通过locale 来设置程序运行的不同语言环境,locale 是(Local Environment )的缩写,它是一个语言环境数据库。locale 中各子项的命名规则为< 语言>_< 地区>.< 字符集编码> ,如zh_CN.UTF-8zh 代表中文,CN 代表大陆地区,UTF-8 表示字符集编码方式。

locale 环境中,有一组变量,把按照所涉及到的文化传统的各个方面分成12个大类, 代表国际化环境中的不同设置。这12个大类分别是:

1.    LC_CTYPE

定义语言符号类别。用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是多字节,如何打印等。是最重要的一个环境变量。

2.    LC_NUMERIC

非货币的数字显示格式

3.    LC_COLLATE

定义该环境的排序和比较规则

4.    LC_TIME

时间和日期格式

5.    LC_MONETARY

货币单位

6.    LC_MESSAGES

应用程序的提示消息。消息主要是提示信息,错误信息,状态信息,标题,标签,按钮和菜单等

7.    LC_NAME

姓名书写方式

8.    LC_ADDRESS

地址书写方式

9.    LC_TELEPHONE

电话号码书写方式

10.    LC_MEASUREMENT

度量衡表达方式

11.    LC_PAPER

默认纸张尺寸大小

12.    LC_IDENTIFICATION

对locale自身包含信息的概述

设定locale就是设定12大类的locale分类属性,即 12个LC_*。除了这12个变量可以设定以外,为了方便设置,还有两个变量:LC_ALL和LANG。它们之间有一个优先级的关:LC_ALL > LC_* >LANG。可以这么说,LC_ALL是最优先设定或者强制设定,而LANG是默认设定值。

 

A.    LANG

LC_* 的默认值,是最低级别的设置,如果LC_* 没有设置,则使用该值。

B.    LC_ALL

它是一个宏,如果该值设置了,则该值会覆盖所有LC_* 的设置值。注意,LANG 的值不受该宏影响。

一个例子:

设置前,使用默认locale
debian:~# locale

LANG="POSIX"

LC_CTYPE="POSIX"

LC_NUMERIC="POSIX"

LC_TIME="POSIX"

LC_COLLATE="POSIX"

LC_MONETARY="POSIX"

LC_MESSAGES="POSIX"

LC_PAPER="POSIX"

LC_NAME="POSIX"

LC_ADDRESS="POSIX"

LC_TELEPHONE="POSIX"

LC_MEASUREMENT="POSIX"

LC_IDENTIFICATION="POSIX"

LC_ALL=

 

设置后,使用zh_CN.GBK
中文locale
debian:~# export LC_ALL=zh_CN.GBK

debian:~# locale

LANG=zh_CN.UTF-8

LC_CTYPE="zh_CN.GBK"

LC_NUMERIC="zh_CN.GBK"

LC_TIME="zh_CN.GBK"

LC_COLLATE="zh_CN.GBK"

LC_MONETARY="zh_CN.GBK"

LC_MESSAGES="zh_CN.GBK"

LC_PAPER="zh_CN.GBK"

LC_NAME="zh_CN.GBK"

LC_ADDRESS="zh_CN.GBK"

LC_TELEPHONE="zh_CN.GBK"

LC_MEASUREMENT="zh_CN.GBK"

LC_IDENTIFICATION="zh_CN.GBK"

LC_ALL=zh_CN.GBK

"C" 是系统默认的locale"POSIX""C" 的别名。所以当我们新安装完一个系统时,默认的locale 就是CPOSIX

此外:

There are several special environment variables that determine how various locale-specific tasks should be handled.
LANG - Specifies the default locale for all unset locale variables
LANGUAGE - Most programs use this for the language of its interface
LINGUAS - (Obsolete?) The WindowMaker window manager used to use this instead of LANGUAGE.

When a program looks up locale dependent values, it does this according to the following environment variables, in priority order:

  1. LANGUAGE
  2. LC_ALL
  3. LC_xxx , according to selected locale category: LC_CTYPE , LC_NUMERIC , LC_TIME , LC_COLLATE , LC_MONETARY , LC_MESSAGES , ...
  4. LANG

Variables whose value is set but is empty are ignored in this lookup.。

Debian 中安装locales 的方法如下:

·   通过apt-get install locales 命令安装locales

·   安装完成locales 包后,系统会自动进行locale 配置,你只要选择所需的locale ,可以多选。最后指定一个系统默认的locale 。这样系统就会帮你自动生成相应的locale 和配置好系统的locale

·   增加新的locale 也很简单,用dpkp-reconfigure locales 重新配置locale 即可。

·   我们也可手动增加locale ,只要把新的locale 增加到/etc/locale.gen 文件中,再运行locale-gen 命令即可生成新的locale 。再通过设置上面介绍的LC_* 变量就可设置系统的locale 了。下是一个locale.gen 文件的样例。

·      

# This file lists locales that you wish to have built. You can find a list

·      

# of valid supported locales at /usr/share/i18n/SUPPORTED. Other

·      

# combinations are possible, but may not be well tested. If you change

·      

# this file, you need to rerun locale-gen.

·      

#


·      

zh_CN.GBK GBK

·     

zh_CN.GB18030 GB18030

·      

zh_CN.UTF-8 UTF-8

在安装了locale 支持的系统中,在/usr/share/locale 目录下保存locale 的信息,在/usr/share/consolefonts 目录下保存字体信息,在/usr/lib/gconv 目录下保存字符转换模块的信息。总结:如果要在Linux 下正确显示中文信息,需要做以下工作。

·   系统本身要有国际化支持,Linux 的国际化支持是很完善的。

·   安装本地locale ,如:zh_CN.GB2312zh_CN.GBK、zh_CN.GB18030、zh_CN.UTF-8 等。

·   安装中文字体,如:文泉驿和文鼎的中文字体等。

·   设置中文的环境变量,如:LANG=zh_CN.GB2312LANG=zh_CN.UTF-8 等。有几个地方都可以设置locale 环境变量。

o      一个是在X Window 的登录管理器中可以设置,如GDMKDM

o      一个是在X Window Session 初始化时设置,在/etc/X11/Xsession.d 目录下的所有脚本在X Window Session 初始化时都会自动运行,所以我们可把export LANG="zh_CN.GB18030" 这条设置命令放到任意的脚本中。建议放到中文输入法的启动脚本中。示例:(这是我手工创建的启动fcitx 中文输入法的脚本91fcitx

o           

debian:/etc/X11/Xsession.d# cat 91fcitx

o           

export LANG="zh_CN.UTF-8"

o           

export XMODIFIERS="@im=fcitx"

o           

export XIM_PROGRAM=fcitx

o           

export XIM=fcitx

o           

fcitx&

o      shell 的启动脚本中设置,如在.bashrc.bash_profile 等文件中直接加入export LANG="zh_CN.UTF-8" 命令。

o      还可以在shell 中直接用export LANG="zh_CN.UTF-8" 命令设置。但如果使用该命令设置的环境变量只在当前shell 中有效。

·   在应用程序中配置使用中文显示。

要在Shell 中正常显示系统的中文提示信息和支持中文输入。LANGshell 的编码配置需一致,并安装有中文locale 。如:LANGshell 的编码都配置成zh_CN.utf8 ,并安装有zh_CN.utf8 这个locale 。如果shellLANG 配置不同,则中文显示乱码;如果LANG 里设置的locale 没有安装,则不能显示系统的中文提示信息,只会显示英文提示信息。

在不同的locale 环境下会生成具有不同编码的文件,如在gb2312 环境下创建的文件就具有gb2312 编码,在utf-8 环境下创建的文件就具有utf-8 编码。如果我们在gb2312 环境下打开utf-8 编码的文件中文部份就会显示乱码。在Linux 中有一个叫iconv 的程序可以帮助我们进行文件编码的转换工作。下面的示例是把一个使用gb2312 编码的文件转换成utf-8 编码的文件:

debian:~/Desktop# iconv -f gb2312 -t utf-8 7.txt -o 77.txt


-f 选项指定源文件的编码,-t 选项指定转换后文件的编码,7.txt 是要转换的文件,-o 选项指定转换后输出的文件名。

 

另注:

内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的.现在的Windows
在内部统一使用Unicode,然后用代码页适应各种语言,“内码”的概念就比较模糊了。微
软一般将缺省代码页指定的编码说成是内码,在特殊的场合也会说自己的内码是Unicode,
例如在GB18030问题的处理上。

所谓代码页(code page)就是针对一种语言文字的字符编码。例如GBK的code page是CP936,BIG5的code page是CP950,GB2312的code page是CP20936。
微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个code page是无法真正使用的。

Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打
开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows是按照Unicode编码解释、还是按照GBK、或者按照BIG5,又或 者按照ISO8859-1去解释?如果按GBK去解释,就会得到“汉字”两个字。按照其它编码解释,可能找不到对应的字符,也可能找到错误的字符。所谓“ 错误”是指与文本作者的本意不符,这时就产生了乱码。

答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控
制面板的区域选项设置。记事本的“另存为”功能中有一项ANSI,其实就是按照缺省代码页的编码方法保存。

Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使
用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可
以指定charset。有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定charset。如果他使用了 0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会出现乱码。这时需要在这个html文件中加上指定charset的语 句,例如:如果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。
有 时候网页上显示乱码是因为设定的字符集不对(或者没有相应的字符集),例如网页是用UTF-8编码的,设置使用GB2312去浏览,则系统根据 GB2312去找字体,然后在屏幕上显示,当然是一些乱码;至于有些时候浏览的网页能显示一部分汉字,但有很多的地方是方框,原因可能是字体库包含的某个 字符集对应的字体不完整,有些时候会显示不完全,找一个比较全的支持较多字符集的字体就可以了。

GB18030编码研究以及GBK、GB18030与Unicode的映射

GB18030有两个版本:GB18030-2000和GB18030-2005。在本文中,没有指明版本的GB18030是指GB18030-2005。本文讨论了以下问题:

  1. GB2312有682个图形符号,都放在1区。GBK的1区有717个图形符号,5区有 166个图形符号,一共有883个图形符号。GB18030的1区有728个图形符号,5区还是166个符号。那么,GBK的1区在GB2312基础上增 加了哪35个符号?GB18030又增加了哪些符号?
  2. GBK支持21003个汉字与883个图形符号,一共21886个字符。这21886个字符究竟是哪些字符?这21886个字符的编码在GB18030中有什么变化?
  3. GB18030是怎样映射Unicode的全部0x110000个码位的?
  4. GB18030-2000和GB18030-2005在字汇上有什么区别,在编码上有什么区别?
  5. GB18030-2005的双字节区中有2067个码位被映射到Unicode BMP的PUA。这些码位有什么规律?这些码位中定义了多少字符?其实这2067个码位中只定义了24个字符。
  6. GBK的21886个字符中有95个字符被映射到Unicode BMP的PUA。在GB18030中这95个字符的编码有哪些变化?哪些字符保持了原来的编码?
  7. GBK的23940个码位中有多少码位被映射到Unicode BMP的PUA?在GB18030中这些码位的编码有什么变化?

在讨论这些问题前,我们先约定一下码位空间的表示方法。

0 码位空间

0.1 约定

GBK是双字节编码,每个字符用两个字节表示。GB18030是多字节字符集,它的字符可以用一个、两个或四个字节表示。码位空间由各字节的范围确定。例如:GB18030的四字节字符码位空间是:

  • 第一字节在0x81~0xFE之间
  • 第二字节在0x30~0x39之间
  • 第三字节在0x81~0xFE之间
  • 第四字节在0x30~0x39之间

为了表述方便,我们用0x81308130~0xFE39FE39表示这个码位空间。也就是说:在本文中0x81308130~0xFE39FE39所指的并 是从0x81308130到0xFE39FE39的连续2097773834(0xFE39FE39-0x81308130+1)个字节。在本文中,0x81308130~0xFE39FE39所指的是编码的各字节在对应范围内的码位空间,这个码位空间的码位数目是:

(0xFE-0x81+1)*(0x39-0x30+1)*(0xFE-0x81+1)*(0x39-0x30+1)=126*10*126*10=1587600

同理,0xB0A1~0xF7FE代表的码位空间是第一字节在0xB0~0xF7之间,第二字节在0xA1~0xFE之间的所有码位。这个码位空间的码位数目是:

(0xF7-0xB0+1)*(0xFE-0xA1+1)=72*94=6768

这个码位空间就是GBK和GB18030的2区,在这6768个码位中定义了6763个字符。

本文用~表示上述码位空间,用-表示一般的范围,即:

  • 0xA1A1~0xA9FE 表示第一字节在0xA1到0xA9之间,第二字节在0xA1~0xFE之间的846((0xA9-0xA1+1)*(0xFE-0xA1+1)=9*94)个码位。
  • 0xE000-0xF8FF 表示从0xE000-0xF8FF的连续6400(0xF8FF-0xE000+1)个码位。

0.2 习题

读者如果已经理解了上面的约定,请完成下面两个习题:

  1. 习题一:求码位空间0x8140~0xFE7E的码位数目。
  2. 习题二:求码位空间0x8180~0xFEFE的码位数目。

0.3 答案

以下是习题0.2的答案:

  1. 习题一:(0xFE-0x81+1)*(0x7E-0x40+1)=126*63=7938
  2. 习题二:(0xFE-0x81+1)*(0xFE-0x80+1)=126*127=16002

GB18030双字节字符的码位空间就是0x8140~0xFE7E和0x8180~0xFEFE,双字节字符的码 位数目是7938+16002=23940。0x8140~0xFE7E和0x8180~0xFEFE也是GBK的全部码位空间。GBK在这23940个 码位中定义了21886个字符。

1 GBK回顾

1.1 简介

GBK是双字节编码方案。它的码位空间就是前面所说的0x8140~0xFE7E和0x8180~0xFEFE,一共23940个码位。在这23940个码位上定义了21886个字符,包括21003个汉字和883个图形符号。 《Unicode、GB2312、GBK和GB18030中的汉字》 详细讨论了这21003个汉字。本文的第3节会讨论GB2312、GBK和GB18030的图形符号。

GBK的码位空间可以划分为以下区域:

类别 区名 码位范围 码位数 字符数
符号区 1区 0xA1A1~0xA9FE 846 717
5区 0xA840~0xA97E和0xA880~0xA9A0 192 166
汉字区 2区 0xB0A1~0xF7FE 6768 6763
3区 0x8140~0xA07E和0x8180~0xA0FE 6080 6080
4区 0xAA40~0xFE7E和0xAA80~0xFEA0 8160 8160
用户自定义区 用户区1 0xAAA1~0xAFFE 564
用户区2 0xF8A1~0xFEFE 658
用户区3 0xA140~0xA77E和0xA180~0xA7A0 672

1.2 GBK字符与Unicode的映射

我制作了一个Excel文件: 附件1 。这个文件包含3张表格:

  1. 按照GBK编码排序的GBK全部21886字符码表。这个表格有3列:字符、GBK编码、Unicode编码。
  2. 按照Unicode编码排序的GBK全部21886字符码表。这个表格有3列:字符、Unicode编码、GBK编码。
  3. 从 按Unicode编码排序的表格中,很容易找到被映射到PUA(0xE000-0xF8FF)的字符。GBK的21886个字符中有95个字符属于 PUA。第三张表格列出了这95个字符(A列)的GBK编码(B列)、Unicode编码(C列)以及这些字符在GB18030中对应的Unicode编 码(D列)。
    其中D列可能不太容易理解,我再解释一下。GB18030是兼容GBK的,所以这些字符的GBK编码和GB18030编码是相同的。 例如的GBK编码和GB18030编码都是0xA8BF。但是在GBK和GB18030中,被映射到不同的Unicode码位。在GBK 中,0xA8BF被映射到Unicode的0xE7C8。在Unicode中,码位0xE7C8是一个PUA码位,保留给用户使用。在GB18030 中,0xA8BF被映射到Unicode的0x01F9。在Unicode中,码位0x01F9属于“拉丁字母扩充-B”这个Block,这个码位定义的 字符是“带抑音符的拉丁文小写字母 N”,字形就是。

1.3 GBK码位与Unicode的映射

GBK的23940个码位定义了21886个字符,还有23940-21886=2054个空闲码位,这2054个 码位都被映射到Unicode的PUA。在设计GBK时,GBK的21886个字符中有95个在Unicode中没有对应字符,所以这95个字符也被映射 到Unicode的PUA。在GBK的23940个码位中,一共有2054+95=2149个码位被映射到PUA,对应的PUA编码是 0xE000-0xE864。0xE000-0xE864就是2149个码位。这2149个码位的分配有以下规律:

码位所在区域 码位数量 映射到的PUA范围
用户区1:0xAAA1~0xAFFE 564 0xE000-0xE233
用户区2:0xF8A1~0xFEFE 658 0xE234-0xE4C5
用户区3:0xA140~0xA77E和A180-A7A0 672 0xE4C6-0xE765
符号区(1区和5区)的170个空闲码位 170 0xE766-0xE80F
2区的5个空闲码位:0xD7FA-0xD7FE 5 0xE810-0xE814
4区的80个Unicode当时没有定义的字符:FE50-FE7E和FE80-FEA0 80 0xE815-0xE864

附件2 包含两张表格:

  1. 23940个GBK码位与Unicode的映射。两组数据分别按GBK和Unicode排序。
  2. 2149个映射到PUA的码位,按Unicode顺序排列。

2 GB18030编码

2.1 概述

GB18030是多字节字符集,它的字符可以用一个、两个或四个字节表示。GB18030的码位定义如下:

字节数 码位空间 码位数 字符数
单字节 0x00~0x7F 128 128
双字节 0x8140~0xFE7E和0x8180~0xFEFE 23940 21897
四字节 0x81308130~0xFE39FE39 1587600 54531

GB18030有128+23940+1587600=1611668个码位。Unicode的码位数目是0x110000(1114112),少于GB18030。所以,GB18030有足够的空间映射Unicode的所有码位。

GB18030的1611668个码位目前定义了128+21897+54531=76556个字符。Unicode 5.0定义了99089个字符。

2.2 设计思路

GB18030编码可以分为:单字节部分、双字节部分和四字节部分。单字节部分与Unicode的0x00-0x7f完全相同。双字节部分与GBK有两点差异:

  1. 在1区增加了11个字符。这样1区就有717+11=728个字符。增加的11个字符是:一个欧元符号(0xA2E3)和10个竖排标点符号(0xA6D9-0xA6DF、0xA6EC-0xA6ED和0xA6F3)。
  2. 原来因为Unicode没有收录而映射到PUA的字符中的部分字符被新版本的Unicode收录,所以将这些字符映射到非PUA的码位。

Unicode的BMP一共有65536个码位。其中代理区(0xD800-0xDFFF)有2048个码位,这 2048个码位是不能定义字符的。GB18030的单字节部分映射了128个码位,GB18030的双字节部分映射了23940个码位。还剩下 65536-2048-128-23940=39420个码位。

GB18030将这39420个码位顺序映射到从0x81308130开始的码位空间。GB18030将 Unicode的16个辅助平面(0x10000-0x10FFFF,一共1048576个码位)顺序映射到从0x90308130开始的码位空间。 GB18030四字节部分中只有这两个区域定义了字符,其它空间都是保留区和自定义区。本文的第3节和第4节还会详细讨论GB18030的双字节和四字节 部分。

GB18030的设计思路可以概括到以下几点:

  1. 单字节部分与Unicode一致。
  2. 双字节部分与GBK兼容。适当调整一些字符与Unicode的映射。这些字符原来因为Unicode没有收录而被映射到PUA,现在因为Unicode已经收录而调整到非PUA的Unicode码位。
  3. 将Unicode BMP部分还没有映射的39420个码位顺序映射到从0x81308130开始的四字节部分。
  4. 将Unicode BMP以外的16个辅助平面映射到39420个码位顺序映射到从0x90308130开始的四字节部分。

在GB18030目前定义的76556个字符中,只有24个字符被定义到Unicode的PUA区。这24个字符包 括1区的10个竖排标点符号(0xA6D9-0xA6DF、0xA6EC-0xA6ED和0xA6F3)和4区的14个汉字(0xFE51、 0xFE52、0xFE53、0xFE59、0xFE61、0xFE66、0xFE67、0xFE6C、0xFE6D、0xFE76、0xFE7E、 0xFE90、0xFE91、0xFEA0)。4区的14个汉字在Unicode 5.0中其实也可以找到非PUA的编码,详见 《Unicode、GB2312、GBK和GB18030中的汉字》 。但按照GB18030,它们还是应该映射到PUA码位。

2.3 GB18030-2000和GB18030-2005的区别及以后版本

GB18030-2005与GB18030-2000的编码体系结构是完全相同的。GB18030-2005相对于GB18030-2000主要有以下变化:

  1. 在四字节字符表中增加CJK统一汉字扩充B和已经在GB13000中编码的我国少数民族文字字符的字形。其实GB18030-2000已经映射了这些码位,但GB18030-2000没有给出这些字符的字形。
  2. 调整字符的编码。

其中的编码调整比较有意思。的GB18030编码是0xA8BC,在Unicode 5.0的编码是0x1E3F。在GB18030-2000中0xA8BC被映射到Unicode的0xE7C7,因为双字节部分没有映射0x1E3F,所 以它作为BMP的未映射字符被放到四字节部分的0x8135F437。GB18030-2005将0xA8BC映射到0x1E3F,那么Unicode码 位0xE7C7怎么办呢?为了最小化对原来编码的影响,设计者将Unicode码位0xE7C7映射到本来映射0x1E3F的0x8135F437。

GB18030已经映射了Unicode的所有码位,所以不管Unicode怎么变化,GB18030不过就是在现在的码位上增加一些字形而已,编码不会变化。只有现在还映射到PUA的24个字符以后可能会调整到非PUA码位。调整方法应该与的调整方法相同。

2.4 GB18030双字节部分

前面已经介绍过GB18030双字节部分与GBK的区别,本小节再提一些细节。前面也说过,GB18030映射了 Unicode除代理区外的所有码位。所以,Unicode BMP的6400个PUA码位在GB18030中都有对应的码位。GB18030双字节部分映射了2067个PUA码位。

前面说过,GBK映射了2149个PUA码位。现在GB18030双字节部分映射了2067个PUA码位。所以有 2149-2067=82个字符的映射发生了变化。GBK原来有95个字符映射到PUA,其中81个字符在GB18030中被映射到非PUA码位。余下的 14个汉字就是 《Unicode、GB2312、GBK和GB18030中的汉字》 提到的那14个汉字(0xFE51、0xFE52、0xFE53、0xFE59、0xFE61、0xFE66、0xFE67、0xFE6C、0xFE6D、0xFE76、0xFE7E、0xFE90、0xFE91、0xFEA0)。 附件1 列出了这些字符的编码变化。82个映射变化的码位,除了这81个外,还有一个就是欧元符号:GB18030编码是0xA2E3,Unicode编码是0x20AC。码位0xA2E3在GBK中被映射到0xE76C,GBK的码位0xA2E3没有定义字符。

GB18030双字节部分与Unicode的映射没有规律,只能通过查表方法映射。

2.5 GB18030四字节部分

GB18030四字节部分的字符可以见GB18030-2005的“表3 四字节部分的码位安排”,一共54531个字符。GB18030四字节部分的码位可以见GB18030-2005的“7.3 四字节部分字符的排列顺序”。其中定义字符的只有两个区域:

  • GB18030用码位0x81308130~0x8439FE39共50400个码位映射该标准单字节和双字节部分没有映射过的39420个Unicode BMP码位。
  • GB18030用码位0x90308130~0xE339FE39共1058400个码位映射Unicode 16个辅助平面(平面1到平面16)的65536*16=1048576个码位。

为了叙述方便,本文将0x81308130~0x8439FE39称作“BMP扩展部分”,将 0x90308130~0xE339FE39称作“辅助平面部分”。GB18030四字节部分的码位空间是0x81308130~0xFE39FE39。 第二字节有(0x39-0x30+1)=10个可能值。第三字节有(0xFE-0x81+1)=126个可能值。第四字节也是 (0x39-0x30+1)=10个可能值。为了方便下面的演算,本文为这个码位空间定义几个名词:

  • 我们将四字节码位空间中第一字节相同的区域称作一级区 。每个一级区有12600个码位,即:10*126*10。
  • 我们将四字节码位空间中第一字节和第二字节相同的区域称作二级区 。每个二级区有1260个码位,即:126*10。
  • 我们将四字节码位空间中前三个字节相同的区域称作三级区 ,每个三级区有10个码位。

四字节部分一共有(0xFE-0x81+1)=126个一级区。BMP扩展部分有4个一级区。辅助平面部分有84个一级区。还有38个一级区是保留区或自定义区。

2.5.1 BMP扩展部分

BMP扩展部分占据四字节部分开头的4个一级区,一共有4*12600=50400个码位。这段空间的 Unicode映射说起来还是很简单的,就是顺序映射单字节、双字节没有映射过的BMP码位。这些映射关系在GB18030-2000中确定下来。以后的 调整(例如)只是个别字符,不会影响其它字符的位置。但是因为双字节字符已经映射过的BMP码位没有什么规律,所以造成BMP扩展部分的Unicode 映射也不能用公式换算,还是要查表解决。

显然这50400个码位中只用到了39420个码位,其余码位都是保留的。出于好玩,我们来计算一下最后一个非保留码位(0xFFFF)的位置,计算过程如下:

  • m1=(39420-1)/12600=3
  • n1=(39420-1)%12600=1619
  • m2=n1/1260=1619/1260=1
  • n2=n1%1260=1619%1260=359
  • m3=n2/10=359/10=35
  • n3=n2%10=359%10=9
  • 第一字节的位置是:0x81+m1=0x81+3=0x84
  • 第二字节的位置是:0x30+m2=0x30+1=0x31
  • 第三字节的位置是:0x81+m3=0x81+35=0xA4
  • 第四字节的位置是:0x30+n3=0x30+9=0x39

所以Unicode编码0xFFFF映射的GB18030码位是0x8431A439。在BMP扩展部分中,0x8431A439以后的码位都是保留码位。上述计算中,/表示整除(例如5/3=1),%表示取余(例如5%3=2)。

2.5.2 辅助平面部分

辅助平面部分用84个一级区(0x90308130~0xE339FE39)直接映射Unicode的16个辅助平面。这部分映射是可以直接用公式计算的。让我们看看怎么计算。

  • 从Unicode编码到GB18030编码的映射方法如下:
    • U=Unicode编码-0x10000
    • m1=U/12600
    • n1=U%12600
    • m2=n1/1260
    • n2=n1%1260
    • m3=n2/10
    • n3=n2%10
    • 第一字节b1=m1+0x90
    • 第二字节b2=m2+0x30
    • 第三字节b3=m3+0x81
    • 第四字节b4=n3+0x30
    按 照上述方法可以计算出0x10FFFF被映射到0xE3329A35。在辅助平面部分,0xE3329A35以后的码位都是保留码位。以上所写的算法可以 很容易写成C/C++代码。对于不会编程的读者,也可以用Excel公式计算。假设Unicode编码放在单元格A12,计算方法如下:
    • 将m1放在B12,B12=INT((HEX2DEC(A12)-65536)/12600)
    • 将n1放在C12,C12=MOD((HEX2DEC(A12)-65536),12600)
    • 将m2放在D12,D12=INT(C12/1260)
    • 将n2放在E12,E12=MOD(C12,1260)
    • 将m3放在F12,F12=INT(E12/10)
    • 将n3放在G12,G12=MOD(E12,10)
    • 将第一字节放在H12,H12=DEC2HEX(B12+144)
    • 将第二字节放在I12,I12=DEC2HEX(D12+48)
    • 将第三字节放在J12,J12=DEC2HEX(F12+129)
    • 将第四字节放在K12,K12=DEC2HEX(G12+48)
    附件3 中有写好上述公式的Excel表格。使用函数HEX2DEC/DEC2HEX需要通过“工具->加载宏”钩上“分析工具库”。
  • 从GB18030编码到Unicode编码的映射方法如下:
    • 设GB18030编码的四个字节依次为:b1、b2、b3、b4,则
      Unicode编码=0x10000+(b1-0x90)*12600+(b2-0x30)*1260+(b3-0x81)*10+b4-0x30
    假设b1、b2、b3、b4分别放在A4、B4、C4、D4,Unicode编码放在E4,则Excel计算公式为:
    • E4 = =DEC2HEX((HEX2DEC(A4)-144)*12600+(HEX2DEC(B4)-48)*1260+(HEX2DEC(C4)-129)*10+(HEX2DEC(D4)-48)+65536)

2.6 GB18030和Unicode的映射表

附件3 给出了GB18030和Unicode的映射表。这个Excel文件是在网友谢振斌先生的 映射表 基础上制作的,包含3张表格:

  1. 双字节部分23940个码位与Unicode的映射。两组数据分别按GB18030和Unicode排序。
  2. BMP扩展部分39420个码位与Unicode的映射。两组数据分别按GB18030和Unicode排序。
  3. 辅助平面部分,GB18030编码和Unicode编码的映射公式。

3 GB2312、GBK和GB18030中的图形符号

在研究GB18030编码的过程中,我整理了GB2312、GBK和GB18030在1区和5区的图形符号,制作了 附件4 。这个Excel文件包含3张表格:

  1. GB2312的1区字符表。GBK和GB18030的1区、5区字符表。用不同颜色标注了GBK增加的35个字符和GB18030增加的11个字符。
  2. GB2312 1区682个符号的编码。
  3. GBK 1区717个符号的编码。

结束语

通过本文的介绍,读者可以回答开头的问题了吗?

无论是Windows XP还是Vista,中文(中国)区域对应的默认代码页还是GBK。我们只能设置区域,并不能设置区域对应的默认代码页。所以在Windows世界,只要 微软不愿意,GB18030就只是一张普通的代码页。目前的简体中文文档使用的编码主要是Unicode和GBK,本文对GB18030编码所作的一些研 究,希望能对对GB18030感兴趣的读者有所助益。

 

Reference:

GNU `gettext' utilities


Table of Contents

...

1 Introduction

...

2 The User's View

Nowadays, when users log into a computer, they usually find that all their programs show messages in their native language – at least for users of languages with an active free software community, like French or German; to a lesser extent for languages with a smaller participation in free software and the GNU project, like Hindi and Filipino.

How does this work? How can the user influence the language that is used by the programs? This chapter will answer it.

 

2.1 Operating System Installation

...

2.2 Setting the Locale Used by GUI Programs

The immediately available programs in a user's desktop come from a group of programs called a “desktop environment”; it usually includes the window manager, a web browser, a text editor, and more. The most common free desktop environments are KDE, GNOME, and Xfce.

The locale used by GUI programs of the desktop environment can be specified in a configuration screen called “control center”, “language settings” or “country settings”.

Individual GUI programs that are not part of the desktop environment can have their locale specified either in a settings panel, or through environment variables.

For some programs, it is possible to specify the locale through environment variables, possibly even to a different locale than the desktop's locale. This means, instead of starting a program through a menu or from the file system, you can start it from the command-line, after having set some environment variables. The environment variables can be those specified in the next section (Setting the POSIX Locale ); for some versions of KDE, however, the locale is specified through a variable KDE_LANG , rather than LANG or LC_ALL .

2.3 Setting the Locale through Environment Variables

As a user, if your language has been installed for this package, in the simplest case, you only have to set the LANG environment variable to the appropriate ‘ll _ CC ’ combination. For example, let's suppose that you speak German and live in Germany. At the shell prompt, merely execute ‘setenv LANG de_DE ’,‘export LANG; LANG=de_DE ’ (in sh) or ‘export LANG=de_DE ’ (in bash).This can be done from your .login or .profile file, once and for all. 

2.3.1 Locale Names

A locale name usually has the form ‘ll _ CC ’. Here ‘ll ’ is an ISO 639 two-letter language code, and ‘CC ’ is an ISO 3166 two-letter country code. For example, for German in Germany, ll is de , and CC is DE . You find a list of the language codes in appendix Language Codes and a list of the country codes in appendix Country Codes .

You might think that the country code specification is redundant. But in fact, some languages have dialects in different countries. For example, ‘de_AT ’ is used for Austria, and ‘pt_BR ’ for Brazil. The country code serves to distinguish the dialects.

Many locale names have an extended syntax ‘ll _ CC . encoding ’ that also specifies the character encoding. These are in use because between 2000 and 2005, most users have switched to locales in UTF-8 encoding. For example, the German locale on glibc systems is nowadays ‘de_DE.UTF-8 ’. The older name ‘de_DE ’ still refers to the German locale as of 2000 that stores characters in ISO-8859-1 encoding – a text encoding that cannot even accomodate the Euro currency sign.

Some locale names use ‘ll _ CC .@ variant ’ instead of ‘ll _ CC ’. The ‘@ variant ’ can denote any kind of characteristics that is not already implied by the language ll and the country CC . It can denote a particular monetary unit. For example, on glibc systems, ‘de_DE@euro ’ denotes the locale that uses the Euro currency, in contrast to the older locale ‘de_DE ’ which implies the use of the currency before 2002. It can also denote a dialect of the language, or the script used to write text (for example, ‘sr_RS@latin ’ uses the Latin script, whereas ‘sr_RS ’ uses the Cyrillic script to write Serbian), or the orthography rules, or similar.

On other systems, some variations of this scheme are used, such as ‘ll ’. You can get the list of locales supported by your system for your language by running the command ‘locale -a | grep '^ ll ' ’.

There is also a special locale, called ‘C ’. When it is used, it disables all localization: in this locale, all programs standardized by POSIX use English messages and an unspecified character encoding (often US-ASCII, but sometimes also ISO-8859-1 or UTF-8, depending on the operating system).

2.3.2 Locale Environment Variables

A locale is composed of several locale categories , see Aspects . When a program looks up locale dependent values, it does this according to the following environment variables, in priority order:

  1. LANGUAGE
  2. LC_ALL
  3. LC_xxx , according to selected locale category: LC_CTYPE , LC_NUMERIC , LC_TIME , LC_COLLATE , LC_MONETARY , LC_MESSAGES , ...
  4. LANG

Variables whose value is set but is empty are ignored in this lookup.

LANG is the normal environment variable for specifying a locale. As a user, you normally set this variable (unless some of the other variables have already been set by the system, in /etc/profile or similar initialization files).

LC_CTYPE , LC_NUMERIC , LC_TIME , LC_COLLATE , LC_MONETARY , LC_MESSAGES , and so on, are the environment variables meant to override LANG and affecting a single locale category only. For example, assume you are a Swedish user in Spain, and you want your programs to handle numbers and dates according to Spanish conventions, and only the messages should be in Swedish. Then you could create a locale named ‘sv_ES ’ or ‘sv_ES.UTF-8 ’ by use of the localedef program. But it is simpler, and achieves the same effect, to set the LANG variable to es_ES.UTF-8 and the LC_MESSAGES variable to sv_SE.UTF-8 ; these two locales come already preinstalled with the operating system.

LC_ALL is an environment variable that overrides all of these. It is typically used in scripts that run particular programs. For example, configure scripts generated by GNU autoconf use LC_ALL to make sure that the configuration tests don't operate in locale dependent ways.

Some systems, unfortunately, set LC_ALL in /etc/profile or in similar initialization files. As a user, you therefore have to unset this variable if you want to set LANG and optionally some of the other LC_xxx variables.

The LANGUAGE variable is described in the next subsection.

2.3.3 Specifying a Priority List of Languages

Not all programs have translations for all languages. By default, an English message is shown in place of a nonexistent translation. If you understand other languages, you can set up a priority list of languages. This is done through a different environment variable, called LANGUAGE . GNU gettext gives preference to LANGUAGE over LC_ALL and LANG for the purpose of message handling, but you still need to have LANG (or LC_ALL ) set to the primary language; this is required by other parts of the system libraries. For example, some Swedish users who would rather read translations in German than English for when Swedish is not available, set LANGUAGE to ‘sv:de ’ while leaving LANG to ‘sv_SE ’.

Special advice for Norwegian users: The language code for Norwegian bokma*l changed from ‘no ’ to ‘nb ’ recently (in 2003). During the transition period, while some message catalogs for this language are installed under ‘nb ’ and some older ones under ‘no ’, it is recommended for Norwegian users to set LANGUAGE to ‘nb:no ’ so that both newer and older translations are used.

In the LANGUAGE environment variable, but not in the other environment variables, ‘ll _ CC ’ combinations can be abbreviated as ‘ll ’ to denote the language's main dialect. For example, ‘de ’ is equivalent to ‘de_DE ’ (German as spoken in Germany), and ‘pt ’ to ‘pt_PT ’ (Portuguese as spoken in Portugal) in this context.

Note: The variable LANGUAGE is ignored if the locale is set to ‘C ’. In other words, you have to first enable localization, by setting LANG (or LC_ALL ) to a value other than ‘C ’, before you can use a language priority list through the LANGUAGE variable.

2.4 Installing Translations for Particular Programs

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值