14.13 国际化问题
C语言在诞生时并没有考虑到不同的国家或地区使用的问题,这为后来的应用带来了许多麻烦。比如说,C语言规定“char”类型为一个字节,但世界上许多国家的文字无法用这种方式编码;有些国家或地区习惯使用的键盘可能根本没有C语言用到的全部字符;此外各个国家或地区的习惯有大差异,比如有的地方把日期写成12/6/64,而另一些地区可能写成6/12/64。
从C89开始,C语言开始逐步考虑解决这些问题,以期C语言能成为一种国际化的语言。C95(C89增补1)在这方面进一步得到了加强,增加了宽字符等方面的内容。C99则容许基本字符集以外的字符构成标识符。
本节介绍的内容主要与这些问题有关。
14.13.1 locale.h
这个标准头的目的是让程序能够适合特定地区的风俗习惯。比如说,按照当地的习惯显示时间。
“locale.h”中定义了下面一种数据类型。
struct lconv:这是一种结构体数据类型,其成员描述特定地区的数值书写习惯,包括货币的书写习惯。比如,小数点如何表示等。
与这种类型有关的函数是
这个函数用来查询数值格式约定(Numeric formatting convention)。
“locale.h”中的另一个函数是
这个函数用于改变运行库的区域设置特性,当然它也可以查询。
该函数的第二个参数可以由编译器自行指定许多可能,标准中只要求必须要有“"DC"”和“""”(空字符串)两种。前一种表示设置成最简环境(minimal environment),后一种表示切换到当地模式(native environment)。
这个函数的第一个实参参数通常用这个标准头中定义的一些宏表示,这些宏都以“LC_”开头。
LC_ALL:表示进行全面设置。
LC——COLLATE:这个设置将影响到strcoll()和strxfrm()这两个字符串比较函数的比较准则。
LC_CTYPE:这个设置影响到字符处理函数(如isdigit())及涉及多字节的函数的行为。
LC_MONETARY:影响到localeconv()函数返回的货币格式信息。
LC_NUMERIC:影响到格式化输入输出以及字符串转换函数中的小数点字符,也影响localeconv()函数返回的非货币格式信息。
LC_TIME:影响到strftime()函数输出的字符串。
14.13.2 ctype.h与wctype.h(C99)
“ctype.h”的函数在前面已经详细介绍过了,之所以在这里提到它是为了和“wctype.h”进行比较。
此外需要再补充一点,isalnum()或toupper()这样的以“is”或“to”开头的标识符都被保留,以备将来扩充时使用。所以不应该使用以“is”或“to”开头的标识符作为自己命名时使用的标识符。
“wctype.h”描述的内容与“宽字符”(Wide character)的分类和转换有关,其作用与“ctype.h”相当,然而处理的字符对象不同。
所谓“宽字符”,是指用多于8位的空间记录其字符编码的字符。这是因为8位对于某些字符集来说远远不够。
有些环境下可能存在这种单字节和多字节混合使用表示字符编码的方法,这就是所谓的多字节字符(Multibyte character)。
通常用宽字符类型在计算机内部存储、处理字符,这是因为宽字符的长度是统一的。但是在输入输出时用多字节字符更为方便,因为输入输出设备通常是面向字节的,这样就出现了着两者之间的转换问题。
“wctype.h”在C89中是不存在的,这个标准头是在C95(C89增补1)中开始出现的。因此1994年颁布的GB/T 15272-94中没有这方面的内容。
“wctype.h”中的函数所处理的主要是“wchar_t类型”数据,这种数据类型是在<stddef.h>里定义的,在本质上它是一种整数类型。在“wctype.h”中定义的类型如下所示。
Wint_t:一种可保存扩展字符集的任何值的整数类型。
wctrans_t:一种标量类型,用于表示当地的字符映射。
wctype_t:一种标量类型,其值可以表示当地的字符分类。
此外,这个标准头中也定义了“WEOF”这样的类似于“EOF”的宏。
其中的函数不再一一介绍。其中包括一些诸如isalnum()这样与isalnum()只差一个字符的分类判断函数,也包括towlower()这样的宽字符转换函数。这些函数与“ctype.h”中的函数是对应的,只是名称上相差一个字符,以及数据类型不同。此外“wctype.h”增加了几个““ctype.h””中没有对应函数的函数。
14.13.3 wchar.h
引入了宽字符之后,很显然也同样有宽字符字符串性质的数据。这样,就需要提供许多处理字符与字符串函数的宽字符版本。
处理“char*”及“char”类型数据的函数被分散地描述于“stdio.h”、“stdllb.h”、“string.h”和“time.h”等标准头中。那些函数的宽字符版本都统一地在“wchar.h”中描述。包括宽字符与多字节字符的输入输入函数,宽字符串与数值之间的相互转换,宽字符串的复制、连接和比较,宽字符串格式化日期时间,多字节字符与宽字符之间的转换。
需要说明的是,有些功能接近的函数在不同的标准头中分别出现。比如“stdlib.h”中的mblen()和“wchar.h”中的mbrlen(),它们在功能上有些微妙的差别。
最后要说的是,C语言只提供了处理这种宽字符和多字节字符的抽象机制,但并没有指定编译器应该采用什么样的字符集和编码方案,这些都需要实现来确定。而离开了具体的字符对象讨论字符处理问题,从某种意义上来说,并不是一个实践的问题,几乎已经成了一个高深的理论问题了。这太难了。还是就此打住吧。
————————————————————
(1) 这些文件与工程中由某个模块编译出的目标文件很相似,不同的是,库里的函数不会被全部链接入可执行文件,而只链接调用到的函数。
(2) 不是所有的编译器都提供这些头文件,有时这些文件的内容被内置于编译器内。
(3) 这个标准通常叫做“C89增补1”或“C95”。
(4) 如果需要有的话。