如何Q在E序中)加入q?Unicode 以实现外语支?/p>
![]() |
|
U别Q?初
Thomas W. Burger
(twburger@bigfoot.com)Thomas Wolfgang Burger Consulting 的老板
2001 q?8 ?01 ?/p>
作ؓ一个计机的多位字W表C系l,Unicode 支持世界上所有语a的编码和转换。这文章说明了 Linux 应用E序中的国际语言支持的重要性,以及规划 Unicode 支持q将之结合到 Linux 应用E序中去的思想?/blockquote>Unicode q不只是一个编E工P它还是一个政ȝ、经的工具。没有结合世界的语言支持的应用程序通常只能被那些能d ASCII 所支持语言的个Z用。这使得建立?ASCII 基础之上的计机技术脱M世界上大部分人。Unicode 允许E序使用世界上Q何一U字W集Q因此它支持所有语a?/p>
Unicode 让程序员为普通h提供用他们本国语ap使用的Y件。这样就不用再学一门外语了Q而且更容易实现计机技术社会和财政上的利益。很Ҏ设想Q如果用户必Mؓ使用因特|浏览器而学习乌都语的话,您就难以看到计算机在国的用。Web 更不会出现了?/p>
Linux 承担了对 Unicode 很大E度上的支持。Unicode 支持被嵌入到内核和代码开发库中。在很大E度上,使用E序中几句简单的命op它们自动的l合C码中?/p>
所有现代字W集的基都是?1968 q以 ANSIX3.4 版本出版的美国信息交换标准码QAmerican Standard Code for Information InterchangeQASCIIQ。一个值得注意的例外是?ASCII 之前定义?IBM 的扩充的二进制编码的十进制交换码QExtended Binary Coded Decimal Information CodeQEBCDICQ。ASCII 是一个编码字W集Qcoded character setQCCSQ,换句话说Q它是整数到字符表示的映。ASCII ~码字符集允许用一个八位(Z二进制的Q用?0 ?1 表示的)字段或字节(2^8 =256Q表C?256 个字W。这是一个高度受限的~码字符集,它不能表C多不同语a的所有字W(如中文和日文Q,不能表示U学W号Q更不能表示古代文字Q神U符号和象Ş文字Q和音乐W号。通过更改一个字节的长度而更大的字W集得以被编码,q似乎有效但完全不切实际。所有的计算机都Z八位字节。解x法是一U字W编码方案(Character encoding schemeQCESQ?用定长或变长的多字节序列能够表示?256 大的?q些数值接着通过~码字符集被映射到它们表C的字符?/p>
Unicode 通常用作涉及双字节字W编码方案的通用术语。Unicode CCS 3.1 的官方称谓是 ISO10646-1 通用多八字节~码字符集(Universal Multiple Octet Coded Character SetQUCSQ。Unicode 3.1 版本d?44,946 个新的编码字W。算?Unicode 3.0 版本已经存在?49,194 个字W,p 94,140 个?/p>
Unicode ~码字符集利用了一个由 128 个三l的l构成的四维~码I间。其中每个组包含 256 个二l^面。每个^面由 256 个一l的行组成,q且每个行有 256 个单元。每个单元在q个~码I间内对一个字W编码,或者被声明为未l用。这U编码概念被UCؓ UCS-4Q四个八位元用来表示指定l、^面、行和单元的每个字符?/p>
W一个^面(W?00 l的W?00 q面Q是基本多语aq面QBasic Multilingual PlaneQBMPQ。BMP 按字母、音节、表意符号和各种W号及数字定义了常规使用的字W。后l的q面用于附加字符或其它还没有发明的编码实体。我们需要这完整的范围去处理世界上的所有语aQ特别是拥有近 64,000 个字W的一些东亚语a?/p>
BMP 被用作双字节的编码字W集Q这U编码字W集定?ISO 10646 UCS-2 格式。ISO 10646 UCS-2 是?UnicodeQƈ且两者相同)。BMPQ像所?UCS q面那样Q包含了 256 行,其中每行包含 256 个单元,字符仅仅按照 BMP 中的行和单元的八位元在单元中被编码?q就允许 16 位编码字W能够被用来书写大多数商业上最重要的语a。UCS-2 不需要代码页切换、代码扩展或代码状态。UCS-2 是一U将 Unicode l合到Y件中的简单方法,但它只限于支?Unicode BMP?/p>
若要?8 位字节表CZ个多?2^8 =256 个字W的字符~码pȝQcharacter coding systemQCCSQ,需要一U字W编码方?character-encoding schemeQCESQ?/p>
![]()
![]()
![]()
![]()
回页?/font>
?UNIX 中,使用得最多的字符~码Ҏ?UTF-8?它考虑CҎ?Unicode 全部和q面的全面支持,而且它仍能正的识别 ASCII。除?UTF-8 的其他选择q有QUCS-4、UTF-16、UTF-7.5、UTF-7、SCSU、HTML ?JAVA?/p>
Unicode 转换格式QUnicode Transformation FormatsQUTFsQ是一U通过映射多字节编码中的值来支持 Unicode 的字W编码方案。本文将分析最行的格??UTF-8 字符~码pȝ?/p>
UTF-8 转换格式正逐步成ؓ一U占dC的交换国际文本信息的ҎQ因为它可以支持世界上所有的语言Q而且它还?ASCII 兼容。UTF-8 使用变长~码。从 0 ?0x7fQ?27Q的字符把自w编码成单字节,而将值更大的字符~码?2 ?6 个字节?/p>
0x00000000 - 0x0000007F: 0 xxxxxxx 0x00000080 - 0x000007FF: 110 xxxxx10 xxxxxx 0x00000800 - 0x0000FFFF: 1110 xxxx10 xxxxxx10 xxxxxx 0x00010000 - 0x001FFFFF: 11110 xxx10 xxxxxx10 xxxxxx 10 xxxxxx 0x00200000 - 0x03FFFFFF: 111110 xx10 xxxxxx10 xxxxxx10 xxxxxx 10 xxxxxx 0x04000000 - 0x7FFFFFFF: 1111110 x10 xxxxxx10 xxxxxx10 xxxxxx 10 xxxxxx10 xxxxxx 字节 10 xxxxxx是一个扩展字节,它的 xxxxxx 位位|被以二q制表示的字W代码号的位所填充。这是能够代表被使用代码的最短的可能的多字节序列?
Unicode 字符版权标记字符 0xA9 = 1010 1001 ?UTF-8 ~码如下所C:
11000010 10101001 = 0xC2 0xA9
“不{于”符号字W?0x2260 = 0010 0010 0110 0000 ~码如下所C:
11100010 10001001 10100000 = 0xE2 0x89 0xA0
通过获取
continuation byte
的值可以看到原始数据:[1110]0010 [10]001001 [10]100000
0010 001001 100000
0010 0010 0110 0000 = 0x2260W一个字节定义后面紧跟的八位元数Q如果是 7F 或更,q就是等L ASCII 倹{每个八位字节以 10 xxxxxx 开_保字节不与 ASCII 的值淆?
![]()
![]()
![]()
![]()
回页?/font>
?Linux q_上?UTF-8 之前Q请信分发包里?glibc 2.2 ?XFree86 4.0 或更新的版本。早先的版本~少 UTF-8 语言环境支持?ISO10646-1 X11 字体?/p>
?UTF-8 发布之前QLinux 用户使用各种不同特定语言的扩?ASCIIQ像Ƨ洲用户?ISO 8859-1 ?ISO 8859-2Q希腊用户?ISO 8859-7Q俄|斯用户使用 KOI-8 / ISO 8859-5/CP1251Q西里尔字母Q。这使得数据交换出现了很多问题,q且需要ؓq些~码之间的差异编写应用Y件。这U语a支持是不完善的,而且数据交换没有l过试。Linux 主要的发行商和应用程序开发者正致力于让主要?UTF-8 格式表示?Unicode 成ؓ Linux 中的标准?/p>
Z识别 Unicode 文gQMicrosoft 所有的 Unicode 文g应该?ZERO WIDTH NOBREAK SPACEQU+FEFFQ字W开头。这作ؓ一个“特征符”或“字节顺序标讎ͼbyte-order markQBOMQ”来识别文g中用的~码和字节顺序。但是,Linux/UNIX q没有?BOMQ因为它会破坏现有的 ASCII 文g的语法约定。在 POSIX pȝ中,选中的语a环境识别了在一个过E中的所有输入输出文件期望的~码形式?/p>
有两U方法可以将 UTF-8 支持d?Linux 应用E序中。第一U方法,数据都以 UTF-8 形式存放在各处,q样软g改动很少Q被动的Q。另一U方法,被读取的 UTF-8 数据用标准的 C 语言库函数{变成为宽字符数组Q{换的Q。在输出Ӟ用函?
wcsrtombs()
使字W串被{变回 UTF-8Q?
清单 1. wcsrtombs()
#include <wchar.h> size_t wcsrtombs (char *dest, const wchar_t **src, size_t len, mbstate_t *ps);
Ҏ的选择取决于应用程序的性质。大多数应用E序可以使用被动的方法操作。这是?UNIX q_上?UTF-8 会如此流行的原因。像
cat
?echo
那样的程序就不需要修攏V字节流仍只是字节流Qƈ没有对它q行M处理。ASCII 字符和控制代码在 UTF-8 语言环境中不改变?通过字节计数对字W进行计数的E序需要一些小的改动。在 UTF-8 中应用程序不对Q何扩展的字节q行计数。如果选择?UTF-8 语言环境QC 语言库的
strlen(s)
函数需要用mbstowcs()
函数来代替:
清单 2. mbstowcs() 函数
#include <stdlib.h> size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n);
strlen
的一U常见用法是估算昄宽度。中文和其它表意W号占用两列位|?wcwidth()
函数用来试每个字符的显C宽度:
清单 3. wcwidth() 函数
#include < wchar.h> int wcwidth(wchar_t wc);
![]()
![]()
![]()
![]()
回页?/font>
在正式情况下Q从 GNU glibc 2.2 开始,wchar_t cd只ؓ 32 位的 ISO 10646 格式数值所特定使用Q与当前使用的语a环境无关。通过 ISO C99 所要求?__STDC_ISO_10646__ 宏的定义作ؓ信号通知应用E序?__STDC_ISO_10646__ 的定义用来指?wchar_t ?Unicode。精的值是一个十q制?yyyymmL 格式的常数。例如,使用Q?/p>
清单 4. 指出 wchar_t ?Unicode
#define __STDC_ISO_10646__ 200104L
是ؓ指出 wchar_t cd的值是?ISO/IEC 10646 和到指定的年月ؓ止的所有修正与技术勘误定义的字符~码表示?/p>
?wchar_t 的利用如q个CZ所C,使用宏确定在 ISO C99 可移植代码中写双引号的方法?/p>
清单 5. 定写双引号的方?/b>
#if __STDC_ISO_10646__ printf("%lc", 0x201c); #else putchar('"'); #fi
Ȁz?UTF-8 的恰当的办法?POSIX 语言环境机制。语a环境是一U包含有兌Y件行为特定文化约定的配置讑֮。它包含了字W编码、日期/旉W号、分c规则以及度量系l。语a环境的名U通常?ISO 639-1 语言、ISO 3166-1 国家或地Z码以及可选的~码名称和其它限定符l成。您可以用命?
locale -a
获取所有安装在pȝ上的语言环境列表Q通常?/usr/lib/locale/Q?如果没有预安?UTF-8 语言环境Q你可以?
localedef
命o生成它。若要ؓ某个特定用户生成q激zM个d语的 UTF-8 语言环境Q请使用如下语句Q?
清单 6. 为特定用L成语a环境
localedef -v -c -i de_DE -f UTF-8 $HOME/local/locale/de_DE.UTF-8 export LOCPATH=$HOME/local/locale export LANG=de_DE.UTF-8
有时候ؓ所有用h?UTF-8 语言环境会很有用。root 用户使用如下指o可以完成:
清单 7. 为每个用L成语a环境
localedef -v -c -i de_DE -f UTF-8 /usr/share/locale/de_DE.UTF-8
若要为每个用户将q个语言环境设ؓ~省|可以以下行d?/etc/profile 文g中:
清单 8. 为所有用戯|缺省的语言环境
export LANG=de_DE.UTF-8
处理多字节字W代码序列的函数行ؓ依赖于当前语a环境?LC_CTYPE cdQ它定了依赖语a环境的多字节~码。?LANG=de_DEQd语)会导致输出按 ISO 8859-1 被格式化。?LANG=de_DE.UTF-8 会把输出格式化成 UTF-8。语a环境讄会导?
printf
中的%ls
格式说明W调?wcsrtombs()
函数以便于将宽字W的参数字符串{换成依赖语言环境的多字节~码。语a环境中的国家或地区标识符如:LC_CTYPE= en_GB Q英国英语)?LC_CTYPE= en_AUQ澳大利亚英语)Q它们之间的差异只在 LC_MONETARY cd中,原因在于货币的名U和打印货币数量的规则不同?L您首选的语言环境讄环境变量 LANG。当一?C E序执行
setlocale()
函数Ӟ
清单 9. setlocale() 函数
#include <stdio.h> #include <locale.h> //char *setlocale(int category, const char *locale); int main() { if (!setlocale(LC_CTYPE, "")) { fprintf(stderr, "Locale not specified. Check LANG, LC_CTYPE, LC_ALL. "); return 1; }
C 语言库将会依ơ测试环境变?LC_ALL、LC_CTYPE ?LANG。其中第一个含值的环境变量决定ؓ LC_CTYPE cd装入哪种语言环境数据。语a环境数据分裂成独立的cd。?LC_CTYPE 定义了字W编码,?LC_COLLATE 定义了排序顺序。我们用 LANG 环境变量为所有类别设|缺省语a环境Q但 LC_* 变量可以用来覆盖单个cd?/p>
您可以用命o
locale charmap
查询当前语言环境中字W编码的名称。如果您?LC_CTYPE cd中成功选取?UTF-8 语言环境Q会输出 UTF-8。命?locale -m
提供一张已安装的所有字W编码名U的列表?如果您用专门的 C 语言库的多字节函数来完成所有外部字W编码和内部使用?wchar_t ~码之间的{换,那么 C 语言库将承担责QQ根?LC_CTYPE 使用正确的编码方式。这甚至不需要程序被明确的编码成当前的多字节~码?/p>
如果需要一个应用程序能明确的支?UTF-8Q或其它~码Q{换方法而不?libc 多字节函敎ͼ则应用程序必ȝ定是否需要激z?UTF-8 模式。带?<langinfo.h> 库头文g的与 X/Open 兼容pȝ可以用如下代码:
清单 10. 当前的语言环境是否使用?UTF-8 ~码
BOOL utf8_mode = FALSE; if( ! strcmp(nl_langinfo(CODESET), "UTF-8") utf8_mode = TRUE;
为检当前语a环境是否使用?UTF-8 ~码。首先必调?
setlocale(LC_CTYPE, "")
函数Q依据环境变量设|语a环境。nl_langinfo(CODESET) 函数也是?locale charmap
命o调用Q从而查扑ֽ前语a环境指定的编码名U?另一U可以用的Ҏ是查询语a环境变量Q?/p>
清单 11. 查询语言环境变量
char *s; BOOL utf8_mode = FALSE; if ((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) || (s = getenv ("LANG"))) { if (strstr(s, "UTF-8")) utf8_mode = TRUE; }
q项试假设 UTF-8 语言环境名称中有值“UTF-8”,但实际情况ƈ不L如此Q所以应该?
nl_langinfo()
Ҏ?
![]()
![]()
![]()
![]()
回页?/font>
为支持世界上的所有语aQ需要一U具有八位字节字W编码策略的字符~码pȝQ它的字W应多于 ASCIIQ一U用无W号字节的扩展版本)?2^8 = 256 个字W。Unicode 是q样一U字W编码系l,它具有由 128 个三l组Q带有由大量字符~码Ҏ的方法支持的 94,140 个定义好的字W|l成的四l编码空_?Linux 中更行的字W编码方案是 Unicode 转换格式 UTF-8?/p>
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
- 误?Unicode 联盟?Unicode 主页Q这里定义了 Unicode 字符之间的行为和关系Qƈ为实现者提供了技术信息?
- 国际标准l织QInternational Organization for StandardizationQISOQ?/font> 是一个由 140 个国家组成的全球性的国家标准C֛联盟?
- ANSI 是个U有的、非营利l织Q它理q调?U.S. 的志愿标准化以及一致性评Ll?
- ISO C99 Draft QAcrobat PDF 格式Q?56 )Q是新的 C 语言标准Q来?Calgary 大学 Ben ?C ~程评?
- 请阅?Roman Czyborra ?Unix 环境下的 Unicode?
- 请阅?IANAQInternet Assigned Numbers AuthorityQ?/font>中的 IANA Charset Registration Procedures?
- 请参?Virginia 大学图书?Robertson Media 中心?Unicode Music Symbols?
- L?graphic representation of the Roadmap to the BMP, Plane 0 of the UCS。这些表包含了由 0 P也就是通用字符集(Universal Character SetQUCSQ的基本多语aq面QBasic Multilingual PlaneQBMPQ实际大的映射l成的。Everson Gunn Teoranta 是一个自 1990 q开办的支持数民族语言团体的Y件和出版公司Q由 Michael Everson ?Marion Gunn 共同建立?
- h?UTF-8 and Unicode FAQ for UNIX/LinuxQMarkus Kuhn 的综合性的 one-stop 信息资源Q关于您如何?POSIX pȝQLinuxQUNIXQ?Unicode/UTF-8?
- h?Technology Appraisals Ltd ?Solution Given by the Universal Character SetQ其中提供了独立的、高质量的有关电子商务系l、电子信息传递、XML、网l和 IT 安全的信息、教育和培训?
- 请阅?Mulberry Technologies, Inc ?Unicode presentation titled?0646 and All That?/font>Q一个专d?SGML ?XML pȝ的电子出版物的咨询公司?
- 请咨?Linux E序员手册上?UTF-8 ?an ASCII compatible multi-byte Unicode encoding?
- 请阅?Unicode Standard Annex#15 Unicode Normalization FormsQ一描写了四种 Unicode 文本标准化格式规范的文档。有了这些格式,{h的(规范或是兼容的)文本会有同L二进制表式。当实现工具在标准化的格式中保留了一个字W串Q可以确保有一个以二进制Ş式表现的独一无二的等价字W串?
- 请阅?man-pages.net 上的
mbstowcs
Q它把多字节字符串{换成了宽字符的字W串Qman-pages.net ?Linux 手册面提供了永久的Z Web 的归档文件?- 请阅?Hewlett Packard 的开发者资源站点的 Linux E序员手册上?
wcsrtombs
Q它能将宽字W的字符串{化ؓ多字节字W串?- 请阅?MKS 工具文档中?
setlocale()
Q它能改变或查询语言环境。MKS 软g公司是在 Windows 环境或?UNIX/Linux ?Windows 环境中用于系l管理和开发的 Windows 自动化工L领先供应商?- 请学?IBM Classes for Unicode (ICU)Q一?C 语言?C++ 语言库,它在许多q_上提供了健壮的和功能完善?Unicode 支持?
- 请参?IBM ?“Introduction to Unicode”站?/font>Q这里深入涵盖了 Unicode 基础知识?
- ?IBM 的关于新兴技术的 alphaWorks站点 。请参阅Q?
- UnicodeCompressorQ这里提供了使用标准 Unicode 压羃Ҏ的压~和解压~?Unicode 文本的工?
- Unicode NormalizerQؓ实现快速排序和搜烦?Java 字符串对象{换ؓ标准 Unicode 格式?
- 请阅?TW Burger 撰写?“Cyrillic in Unicode?/font>?Jim Melnick 撰写?“Multilingual forms in Unicode?/font>Q也?developerWorks上?
- 请在 developerWorks上浏?更多 Linux 参考资?/font>?
![]()
![]()
TW Burger ?1979 qv曄做过~程、讲授中{计机评以及撰写有关计算机技术方面的书。他正在l营一个信息技术咨询公司。您可以通过 twburger@bigfoot.com 与他联系?
]]>