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