本章讨论 Solaris 操作系统中包含的若干个国际化功能。本章包含以下主题:
EUC 是扩展 UNIX® 代码的缩写。Solaris 操作系统 支持非 EUC 编码,如日本的 PC-Kanji(更常用的叫法是 Shift_JIS)、中国台湾地区的 Big5 以及中华人民共和国的 GBK。由于很大一部分计算机市场需要非 EUC 代码集支持,所以当前 Solaris 环境提供一个完整的框架,以同时支持 EUC 和非 EUC 代码集。这种支持称为代码集独立性 或 CSI。
CSI 的目标是从 Solaris 操作系统 库和命令中移除对特定代码集或编码方法的依赖性。CSI 体系结构使 Solaris 操作系统 可支持所有 UNIX 文件系统安全编码。CSI 支持许多新的代码集,如 UTF-8、PC-Kanji 和 Big5。
代码集独立性使应用程序和平台软件开发者能够使其代码独立于任何编码(如 UTF-8),同时 CSI 还提供了无须修改源代码即可采用任何新编码的能力。该体系结构方法与 JavaTM 国际化的不同之处在于:应用程序无需依赖于 UTF-16。
许多现有的国际化应用程序(例如,Motif)自动从基础系统继承 CSI 支持。这些应用程序无须修改即可在新的语言环境中工作。
CSI 本身独立于任何代码集。但是,下列关于文件代码编码(代码集)的假定依然适用于当前 Solaris 系统:
本节列出了当前 Solaris 环境中具有 CSI 特征的命令。每个命令的手册页都有一个属性部分,指明该命令是否具有 CSI 特征。
所有命令都位于 /usr/bin 目录中,除非另有说明。
libc (/usr/lib/libc.so) 中的几乎所有的函数都具有 CSI 特征。但是,libc 中的下列函数不具有 CSI 特征,因此它们是依赖于 EUC 的函数:
csetcol()
csetlen()
csetno()
euccol()
euclen()
eucscol()
getwidth()
wcsetno()
在当前 Solaris 环境中,libgen /usr/ccs/lib/libgen.a 和 libcurses /usr/ccs/lib/libcurses.a 已经被国际化,但不具有 CSI 特征。
语言环境数据库的格式和结构是专用的,并且在将来的发行版中可能被修改。在开发国际化应用程序时,请使用 libc 中的国际化 API。在libc 中的国际化 API中,而不是在指向语言环境数据库的链接中介绍这些 API。
在 Solaris 环境中工作时,请使用当前 Solaris 发行版附带的语言环境数据库。不要使用以前的 Solaris 版本中的语言环境。
进程代码格式(在 Solaris 操作系统 产品中,也称为宽字符代码格式)是专用的,并且在将来的发行版中可能被修改。因而,在开发国际应用程序时,不要假定进程代码格式是相同的。相反,请使用 libc 中的国际化 API,如libc 中的国际化 API 中所述。
所有 Unicode 语言环境的进程代码都在 UTF 32 表示法中。有关 UTF 32 的详细信息,请参阅 Unicode 标准附件 19 号:UTF 32 和 Unicode 标准附件 27 号:Unicode 3.1(它们可以从 Unicode 协会或 http://www.unicode.org/ 得到)。
多字节字符是无法存储在单个字节中的字符,如中文、日语或朝鲜语字符。这些字符需要 2、3 或 4 个字节的存储空间。可在 ISO/IEC 9899:1990 3.13 款中找到更准确的定义。
ANSI C 修订 1(也称为 ISO/IEC 9899:1990)添加了新的国际化特征,一并称为多字节支持环境 (MSE)。修订 1 为具有状态的多字节代码集定义了附加的国际化 API,这同时也是为了更好地支持宽字符处理。
程序设计模型使这些多字节字符能够作为逻辑单元读入,然后在内部存储为宽字符。程序可将这些宽字符作为逻辑实体进行处理。最后,这些宽字符可在经过适当的转换后作为逻辑单元写出。
此过程类似于将单字节字符读入、进行处理再写出的方式。MSE 使人们能够使用与处理单字节字符相同的程序设计模型来处理多字节字符的程序。
可以使用动态链接或静态链接,将应用程序与系统库(如 libc)链接。必须动态链接所有需要系统库中的国际化功能的应用程序。如果应用程序是被静态链接的,则在使用 setlocale 函数将语言环境设置为除 C 和 POSIX 以外的语言环境时,操作将失败。静态链接的应用程序只能在 C 和 POSIX 语言环境中运行。
缺省情况下,链接程序将尝试动态链接应用程序。如果链接程序和编译程序的命令行选项包括 -Bstatic 或 -dn 指定内容,则应用程序可能被静态链接。可以使用 /usr/bin/ldd 命令检查现有的应用程序是否为动态链接的。
例如,下列命令的响应表明 /sbin/sh 命令不是动态链接的程序:
| % /usr/bin/ldd /sbin/sh ldd: /sbin/sh: file is not a dynamic executable or shared object | 
下列命令的响应表明 /usr/bin/ls 命令已经与以下两个库动态链接:libc.so.1 和 libdl.so.1。
| % /usr/bin/ldd /usr/bin/ls libc.so.1 => /usr/lib/libc.so.1 libdl.so.1 => /usr/lib/libdl.so.1 | 
libw 和 libintl 已移至 libc,不再位于 libw 和 libintl 中。
共享对象确保现有应用程序的运行兼容性,并且和归档文件一起为生成应用程序提供了编译环境兼容性。然而,您不必再针对 libw 或 libintl 生成应用程序。
以下列表说明了 libw 中的桩模块入口点:
以下列表说明了 libintl 中的桩模块入口点:
字符分类和字符转换宏已在 /usr/include/ctype.h 中进行定义。当前 Solaris 环境提供一组 ctype 宏,该组宏支持由 XPG4 定义的字符分类和转换语义。要使所有 XPG4 和 XPG4.2 应用程序自动访问新的宏,必须满足下列条件之一:
定义了 _XPG4_CHAR_CLASS。
已经定义 _XOPEN_SOURCE 和 _XOPEN_VERSION=4。
已经定义 _XOPEN_SOURCE 和 _XOPEN_SOURCE_EXTENDED=1。
因为 _XOPEN_SOURCE、_XOPEN_VERSION 和 _XOPEN_SOURCE_EXTENDED 除了引入新的 ctype 宏以外,还引入了其他的 XPG4 相关功能,所以非 XPG4 或 XPG4.2 应用程序应该使用 __XPG4_CHAR_CLASS__。
还存在相应的 ctype 函数。当前 Solaris 环境函数也支持 XPG4 语义。
当前 Solaris 环境提供两组 API:
多字节(文件代码)
宽字符(进程代码)
宽字符代码是固定宽度的逻辑实体单元。因此,在使用多字节字符时,不必跟踪维护正确的字符边界。
当程序从文件获取输入时,可以使用 fscanf 和 fwscanf 等输入函数直接将文件的多字节数据转换为宽字符进程代码,或者在输入后使用 mbtowc 和 mbsrtowcs 等转换函数进行转换。要将输出数据从宽字符格式转换为多字节字符格式,请使用 fwprintf 和 fprintf 等输出函数,或者在输出之前应用 wctomb 和 wcsrtombs 等转换函数。
本章剩余部分中的表格介绍了当前 Solaris 系统中包含的国际化 API。
下表介绍了 libc 中的消息传送函数 API。
表 2–1 libc 中的消息传送函数| 库例程 | 说明 | 
|---|---|
| bindtextdomain() | 绑定信息域的路径 | 
| catclose() | 关闭信息目录 | 
| catgets() | 读取程序信息 | 
| catopen() | 打开信息目录 | 
| dcgettext() | 指定了域和种类,从信息目录中获取信息 | 
| dgettext() | 指定了域,从信息目录中获取信息 | 
| gettext() | 从信息数据库中检索文本字符串 | 
| textdomain() | 设置并查询当前域 | 
下表介绍了 libc 中的代码转换函数 API。
表 2–2 libc 中的代码转换函数| 库例程 | 说明 | 
|---|---|
| iconv() | 转换代码 | 
| iconv_close() | 解除分配转换描述符 | 
| iconv_open() | 分配转换描述符 | 
下表介绍了 libc 中的正则表达式 API。
表 2–3 libc 中的正则表达式| 库例程 | 说明 | 
|---|---|
| fnmatch() | 匹配文件名或路径名 | 
| regcomp() | 编译正则表达式 | 
| regerror() | 提供从错误代码到错误信息的映射 | 
| regexec() | 执行正则表达式匹配 | 
| regfree() | 释放由 regcomp() 分配的内存 | 
下表介绍了 libc 中的宽字符函数 API。
表 2–4 libc 中的宽字符类| 库例程 | 说明 | 
|---|---|
| wctrans() | 定义字符映射 | 
| wctype() | 定义字符类 | 
下表列出 libc 中的修改和查询语言环境。
表 2–5 libc 中的修改和查询语言环境函数| 库例程 | 说明 | 
|---|---|
| setlocale() | 修改和查询程序的语言环境 | 
表 2–6 libc 中的查询语言环境数据
| 库例程 | 说明 | 
|---|---|
| localeconv() | 获取当前语言环境的货币和数字格式信息 | 
| nl_langinfo() | 获取当前语言环境的语言和文化信息 | 
下表介绍了 libc 中的字符分类函数 API。
表 2–7 libc 中的字符分类和拼写| 库例程 | 说明 | 
|---|---|
| isalnum() | 字符是字母或数字吗? | 
| isalpha() | 字符是字母吗? | 
| isascii() | 字符是 ASCII 字符吗? | 
| iscntrl() | 字符是控制字符吗? | 
| isdigit() | 字符是数字吗? | 
| isenglish() | 宽字符在辅助代码集的英语字母表中吗? | 
| isgraph() | 字符是可见字符吗? | 
| isideogram() | 宽字符是表意符号吗? | 
| islower() | 字符是小写吗? | 
| isnumber() | 宽字符是辅助代码集中的数字吗? | 
| isphonogram() | 宽字符是音标吗? | 
| isprint() | 字符是可打印字符吗? | 
| ispunct() | 字符是标点符号吗? | 
| isspace() | 字符是空格吗? | 
| isspecial() | 特殊宽字符在辅助代码集中吗? | 
| isupper() | 字符是大写吗? | 
| iswalnum() | 宽字符是字母字符或数字吗? | 
| iswalpha() | 宽字符是字母吗? | 
| iswascii() | 宽字符是 ASCII 字符吗? | 
| iswcntrl() | 宽字符是控制字符吗? | 
| iswdigit() | 宽字符是数字吗? | 
| iswgraph() | 宽字符是可见字符吗? | 
| iswlower() | 宽字符是小写吗? | 
| iswprint() | 宽字符是可打印字符吗? | 
| iswpunct() | 宽字符是标点符号吗? | 
| iswspace() | 宽字符是空白吗? | 
| iswupper() | 宽字符是大写吗? | 
| iswxdigit() | 宽字符是十六进制数字吗? | 
| isxdigit() | 字符是十六进制数字吗? | 
| tolower() | 将大写字符转换为小写字符。 | 
| toupper() | 将小写字符转换为大写字符。 | 
| towctrans() | 宽字符映射。 | 
| towlower() | 将大写宽字符转换为小写宽字符。 | 
| towupper() | 将小写宽字符转换为大写宽字符。 | 
下表介绍了 libc 中的字符排序函数 API。
表 2–8 libc 中的字符排序| 库例程 | 说明 | 
|---|---|
| strcoll() | 排序字符串 | 
| strxfrm() | 变换字符串以便进行比较 | 
| wcscoll() | 排序宽字符串 | 
| wcsxfrm() | 变换宽字符串以便进行比较 | 
下表介绍了 libc 中的货币处理函数 API。
表 2–9 libc 中的货币格式| 库例程 | 说明 | 
|---|---|
| localeconv() | 获取当前语言环境的货币格式信息 | 
| strfmon() | 将货币值转换为字符串表示形式 | 
下表介绍了 libc 中的日期和时间格式。
表 2–10 libc 中的日期和时间格式| 库例程 | 说明 | 
|---|---|
| getdate() | 转换用户格式日期和时间。 | 
| strftime() | 将日期和时间转换为字符串表示形式。%u 转换函数符合“系统界面和标题”的第 4 期,版本 2 中的 X/Open CAE 规范。该函数使用十进制数字 [1,7] 表示一周中的每一天,现在,1 表示星期一。 | 
| strptime() | 日期和时间转换。 | 
下表介绍了 libc 中的多字节处理函数 API。
表 2–11 libc 中的多字节处理| 库例程 | 描述 | 
|---|---|
| btowc() | 单字节到宽字符转换 | 
| mblen() | 获取字符中的字节数 | 
| mbrlen() | 获取字符中的字节数(可重新开始) | 
| mbrtowc() | 将字符转换为宽字符代码(可重新开始) | 
| mbsinit() | 确定转换对象状态 | 
| mbsrtowcs() | 将字符串转换为宽字符串(可重新开始) | 
| mbstowcs() | 将字符串转换为宽字符串 | 
| mbtowc() | 将字符转换为宽字符代码 | 
下表介绍了 libc 中的宽字符和字符串处理。
表 2–12 libc 中的宽字符和字符串处理| 库例程 | 描述 | 
|---|---|
| wcrtomb() | 将宽字符代码转换为字符(可重新开始) | 
| wcscat() | 并置宽字符串 | 
| wcschr() | 在宽字符串中查找字符 | 
| wcscmp() | 比较宽字符串 | 
| wcscpy() | 复制宽字符串 | 
| wcscspn() | 返回一个宽字符串不在另一个宽字符串中的跨度 | 
| wcslen() | 获取宽字符串的长度 | 
| wcsncat() | 并置宽字符串,并置长度为 n | 
| wcsncmp() | 比较宽字符串,比较长度为 n | 
| wcsncpy() | 复制宽字符串,复制长度为 n | 
| wcspbrk() | 返回指向一个位于另一个宽字符串中的宽字符串的指针 | 
| wcsrchr() | 从右边开始在宽字符串中查找字符 | 
| wcsrtombs() | 将宽字符串转换为字符串(可重新开始) | 
| wcsspn() | 返回一个宽字符串在另一个宽字符串中的跨度 | 
| wcstod() | 将宽字符串转换为双精度 | 
| wcstok() | 在整个宽字符串中移动标记 | 
| wcstol() | 将宽字符串转换为长整数 | 
| wcstombs() | 将宽字符串转换为多字节字符串 | 
| wcstoul() | 将宽字符串转换为无符号长整数 | 
| wscwcs() | 在宽字符串中查找字符串 | 
| wcswidth() | 确定宽字符串的列位置数 | 
| wctob() | 宽字符到单字节转换 | 
| wctomb() | 将宽字符转换为多字节字符 | 
| wcwidth() | 确定宽字符的列位置数 | 
| wscol() | 返回宽字符串的显示宽度 | 
| wsdup() | 复制宽字符串 | 
下表介绍了 libc 中的格式化宽字符输入和输出。
表 2–13 libc 中的格式化宽字符输入和输出| 库例程 | 描述 | 
|---|---|
| fwprintf() | 打印格式化宽字符输出 | 
| fwscanf() | 转换格式化宽字符输入 | 
| swprintf() | 打印格式化宽字符输出 | 
| swscanf() | 转换格式化宽字符输入 | 
| vfwprintf() | stdarg 参数列表的宽字符格式化输出 | 
| vswprintf() | stdarg 参数列表的宽字符格式化输出 | 
| wprintf() | 打印格式化宽字符输出 | 
| wscanf() | 转换格式化宽字符输入 | 
| wsprintf() | 根据格式生成宽字符串 | 
| wsscanf() | 格式化输入转换 | 
下表介绍 libc 中的宽字符串函数 API。
表 2–14 宽字符串 libc| 库例程 | 描述 | 
|---|---|
| wcsstr() | 查找宽字符子串 | 
| wmemchr() | 在内存中查找宽字符 | 
| wmemcmp() | 在内存中比较宽字符 | 
| wmemcpy() | 在内存中复制宽字符 | 
| wmemmove() | 在具有重叠区域的内存中复制宽字符 | 
| wmemset() | 在内存中设置宽字符 | 
| wscasecmp() | 比较宽字符串,忽略大小写差异 | 
| wsncasecmp() | 进程代码串操作 | 
下表介绍了 libc 中的宽字符输入和输出。
表 2–15 libc 中的宽字符输入和输出| 库例程 | 说明 | 
|---|---|
| fgetwc() | 从流中获取多字节字符,并转换为宽字符 | 
| fgetws() | 从流中获取多字节串,并转换为宽字符 | 
| fputwc() | 将宽字符转换为多字节字符,并放入流中 | 
| fputws() | 将宽字符转换为多字节串,并放入流中 | 
| fwide() | 设置流定向 | 
| getwchar() | 从 stdin 中获取多字节字符,并转换为宽字符 | 
| getws() | 从 stdin 中获取多字节串,并转换为宽字符 | 
| putwchar() | 将宽字符转换为多字节字符,并放入 stdin 中 | 
| putws() | 将宽字符转换为多字节串,并放入 stdin 中 | 
| ungetwc() | 将宽字符推送回输入流中 | 
新的 genmsg 实用程序可以与 catgets() 系列函数一起使用,以创建国际化的源信息目录。该实用程序在源程序文件中检查对 catgets 中函数的调用,然后根据找到的信息生成源信息目录。例如:
% cat example.c ... /* NOTE: %s is a file name */ printf(catgets(catd, 5, 1, "%s cannot be opened.")); /* NOTE: "Read" is a past participle, not a present tense verb */ printf(catgets(catd, 5, 1, "Read")); ... % genmsg -c NOTE example.c The following file(s) have been created. new msg file = "example.c.msg" % cat example.c.msg $quote " $set 5 1 "%s cannot be opened" /* NOTE: %s is a file name */ 2 "Read" /* NOTE: "Read" is a past participle, not a present tense verb */
在上面的实例中,对源文件 example.c 运行 genmsg,产生一个名为 example.c.msg 的源信息目录。带 NOTE 参数的 -c 选项使 genmsg 在目录中包含注释。如果源程序中的注释包含指定的字符串,则该注释在信息目录中出现在下一个从对 catgets 的调用中提取的字符串的后面。
可以使用 genmsg 对信息集中的信息进行自动编号。
要生成格式化信息目录文件,请使用 gencat (1) 实用程序。
有关可移植消息文件(.po 文件)的消息提取实用程序的信息,以及如何从 .po 文件生成消息对象文件(.mo 文件)的信息。
您可以使用 geniconvtbl 实用程序来创建用户定义的代码集转换器。
使用该实用程序,可以使用标准系统实用程序和接口(如 iconv(1) 和 iconv(3C)),完成用户定义的和用户可定制代码集的转换。该功能增强了应用程序处理不兼容的数据类型特别是从专用或传统应用程序生成的数据的能力。同时也支持对现有的 Solaris 代码集转换的修改。
/usr/lib/iconv/geniconvtbl/srcs/ 目录中提供该实用程序的输入源文件实例。
正确准备和放置用户定义的代码转换后,用户可以使用 32 位和 34 位 Solaris 操作系统 的 iconv(1) 实用程序和 iconv(3C) 函数进行代码转换。
利用国际化域名 (IDN) 功能,可以使用非英语的本机语言名称作为主机名和域名。要使用非英语的主机名和域名,根据 RFC 3490 的规定,请在将名称发送至解析器路由之前将这些名称转换为使用 ASCII 兼容编码 (ACE) 编码的名称。在系统管理应用程序不支持 IDN 的系统文件和应用程序中,系统管理员仍然需要使用 ACE 名称。
请参见 RFC 3490“应用程序中的国际化域名 (IDNA)”。
libidnkit(3EXT) 中的国际化域名 API 提供 UTF-8 或应用程序语言环境的字符集和 ACE 的方便转换。如果使用了 idn_decodename2(3EXT),您还可以指定任意代码集名称作为输入参数的代码集。

 表 2–16  iconv 代码转换
表 2–16  iconv 代码转换| 源代码 | 目标代码 | 
|---|---|
| ACE ACE-ALLOW-UNASSIGNED | UTF-8 UTF-8 | 
| UTF-8 UTF-8 | ACE ACE-ALLOW-UNASSIGNED | 
ACE 和 ACE-ALLOW-UNASSIGNED iconv 代码转换名称具有下列意义:
ACE。
ACE 是可在 iconv 代码转换中使用的 fromcode 或 tocode 名称,指 RFC 3490 中定义的 ASCII 兼容编码。该转换使用 STD3 ASCII 规则。不允许使用未分配字符。ACE 通常用于将主机名或域名存储在或传递到计算机上。
ACE-ALLOW-UNASSIGNED。
ACE-ALLOW-UNASSIGNED 与 ACE 执行相同操作,只是 ACE-ALLOW-UNASSIGNED 允许使用未分配的字符。ACE-ALLOW-UNASSIGNED 通常用于查询。
下例显示将输入的 hostnames.txt 文件从 ACE 转换为至 UTF-8。输出将变为标准输出。
system% iconv -f ACE -t UTF-8 hostnames.txt
专用 IDN 转换实用程序 idnconv(1) 提供具有不同选项的 IDN 转换。这些选项控制转换细节。
有关 IDN、转换例程和 iconv 代码转换的信息,请参见 libidnkit(3LIB)、idn_decodename(3EXT)、idn_decodename2(3EXT)、idn_encodename(3EXT) 和 iconv_en_US.UTF-8(5) 手册页。