在 Windows CRT 的实现中还有一个使用 wchar_t 作为 locale 名的宽字符版本:_wsetlocale()。因此,也有了使用 _TCHAR 宏版本的 setlocale():_tsetlocale()。
Windows CRT 实现的 setlocale() 和 glibc 版本的头文件与声明相同,使用方法类似,如下:
支持的 locale 分类常量:LC_ALL、LC_COLLATE、LC_CTYPE、LC_MONETARY、LC_NUMERIC、LC_TIME。
请求设定的 locale 名可以为以下格式(参考MSDN:Language and Country/Region Strings):
lang[country_region[.code_page]]:虽然形式与 glibc 的相同,当 Windows 的 locale 名并不符合 POSIX 的规范,比如采用 GBK 字符集的大陆中文,POSIX 的名字为:zh_CN.GBK,而在 Windows CRT 中要用:Chinese_People’s Republic of China.936,(--^)。
lang 字段的可用值参考:Language Strings
country_region 字段的可用值参考:Country/Region Strings
code_page 字段的可用值是 Windows 系统支持的代码页编号,参考:Code Page Identifiers
.code_page:可以直接使用代码页来设定 locale,而且可以使用 .OCP、.ACP 两个伪代码页,.OCP 表示从系统获得的当前活动的 OEM 代码页,.ACP 表示从系统获得的活动 ANSI 代码页。
“”:根据 Windows 系统环境的活动 ANSI 代码页来设定 locale。.OCP、.ACP、和环境代码页都受控制面板中“区域与语言选项”的设置影响。默认装完简体中文版 Windows 后,活动的 ANSI 代码页为:936(即 GBK),可用 chcp 控制台程序查看活动代码页。
NULL:取回当前 locale,不改变当前 locale。
setlocale()的作用和使用例子
当向终端、控制台输出 wchar_t 类型的字符时,需要设置 setlocale(),因为通常终端、控制台环境自身是不支持 UCS 系列的字符集编码的,使用流操作函数时(如:printf()),在标准/RT库实现的内部会将 UCS 字符转换成合适的本地 ANSI 编码字符,转换的依据就是 setlocale() 设定的活动 locale,最后将结果字符序列传递给终端,对于来自终端的输入流这个过程刚好相反。
可以用重定向输出流到文件的方法验证上面的机制:无论是 Windows CRT、Linux glibc、Cygwin glibc,使用 wprintf() 打印 wchar_t 字符文本时,重定向到文件的内容总是 GBK、UTF-8 等本地 ANSI 编码,而不会是 UCS 编码。
代码块
01 #ifdef GNUC
02
03 #define CSET_GBK “GBK”
04 #define CSET_UTF8 “UTF-8”
05
06 #define LC_NAME_zh_CN “zh_CN”
07
08 // ifdef GNUC
09 #elif defined(_MSC_VER)
10
11 #define CSET_GBK “936”
12 #define CSET_UTF8 “65001”
13
14 #define LC_NAME_zh_CN “Chinese_People’s Republic of China”
15
16 // ifdef _MSC_VER
17 #endif
18
19 #define LC_NAME_zh_CN_GBK LC_NAME_zh_CN “.” CSET_GBK
20 #define LC_NAME_zh_CN_UTF8 LC_NAME_zh_CN “.” CSET_UTF8
21 #define LC_NAME_zh_CN_DEFAULT LC_NAME_zh_CN_GBK
22
23 void print_current_loc();
24
25 int main(int argc, char* argv[])
26 {
27 char* locname = NULL;
28 const wchar_t* strzh = L”中文字符串”;
29
30 print_current_loc();
31
32 // 使用指定的 locale
33 locname = setlocale(LC_ALL, LC_NAME_zh_CN_DEFAULT);
34 if ( NULL == locname )
35 {
36 printf(“setlocale() with %s failed.\n”, LC_NAME_zh_CN_DEFAULT);
37 }
38 else
39 {
40 printf(“setlocale() with %s succeed.\n”, LC_NAME_zh_CN_DEFAULT);
41 }
42
43 print_current_loc();
44
45 wprintf(L”Zhong text is: %ls\n”, strzh);
46
47 // 使用运行环境中的 locale 设置
48 locname = setlocale(LC_ALL, “”);
49 if ( NULL == locname )
50 {
51 printf(“setlocale() from environment failed.\n”);
52 }
53 else
54 {
55 printf(“setlocale() from environment succeed.\n”);
56 }
57
58 print_current_loc();
59
60 wprintf(L”Zhong text is: %ls\n”, strzh);
61
62 puts(“End of program.”);
63 return 0;
64 }
65
66 // 打印当前 locale
67 void print_current_loc()
68 {
69 char* locname = setlocale(LC_ALL, NULL);
70 printf(“Current locale is: %s\n”, locname);
71 }
脚注
要使上面程序成功编译并执行,需要注意一下几点:
Windows CRT 是不支持 UTF-8 编码作为 locale 的,运行时使用 setlocale(LC_ALL, “.65001”) 会失败。
使用 Linux 和 Cygwin 的 glibc 时,要在终端显示正确的中文,需满足以下条件:
不要混用 char 和 wchar_t 版本的流操作函数,否则会导致这些函数运行异常,我用Cygwin GCC 4测试混用 printf() 和 wprintf() 时,程序甚至崩掉,所以要将上面程序中 printf() 语句全注释掉才行。Window CRT 的实现则没有这个问题。
运行环境的 locale 设置要和程序中 setlocale() 设定的 locale 一致,比如:终端的活动字符集、环境变量(一般用 LANG),要设置为 *.UTF-8,才能显示 setlocale(LC_ALL, “zh_CN.UTF-8”) 设定的 wchar_t 的中文字符。
用 GCC 编译时,要使用 UTF-8 编码保存源文件,这是 GCC 在编译时,将 wchar_t 文字量(以 L 打头)正确转换为 UCS 编码保存在对象文件中的必需条件,用 Native ANSI 编码(比如:GBK)有 wchar_t 文字量的源文件时,GCC 会编译出错,Linux 和 Cygwin 的 GCC 都有这个约束。另外在 Linux GCC 使用 UCS-4 编码保存 wchar_t,而 Windows 和 Cygwin GCC 使用 UCS-2。
用 wprintf() 时,要用 %ls 表示 wchar_t 的字符串,用 %s 表示 char 的字符串,具体参考:man 3 wprintf,而 Windows 的实现用 %ls、%s 都可以正确输出 wchar_t 字符串。