C 和 C++ 的標準庫分別有自己的 locale 操作方法,C 標準庫的 locale 設定函式是 setlocale(),而 C++ 標準庫有 locale 類和流物件的 imbue() 方法(gcc使用zh_CN.GBK,或者zh_CN.UTF-8,VC++使用Chinese_People
轉自:http://zyxhome.org/wp/cc-prog-lang/c-stdlib-setlocale-usage-note/
[在此向原文作者說聲謝謝!若有讀者看到文章轉載時請寫該轉載地址,不要寫我的BLOG地址。尊重他人的勞動成果 ^_^ ]
C 和 C++ 的標準庫分別有自己的 locale 操作方法,C 標準庫的 locale 設定函式是 setlocale(),而 C++ 標準庫有 locale 類和流物件的 imbue() 方法。這篇是我自己的 setlocale() 使用總結。
Linux的glibc中的setlocale()
具體參考:man 3 setlocale
標頭檔案與宣告如下:
1 |
#include <locale.h> |
2 |
char * setlocale ( int category, const char * locale); |
說明:
category:為locale分類,表達一種locale的領域方面,通常有下面這些預定義常量:LC_ALL、LC_COLLATE、LC_CTYPE、LC_MESSAGES、LC_MONETARY、LC_NUMERIC、LC_TIME,其中 LC_ALL 表示所有其它locale分類的並集。
locale:為期望設定的locale名稱字串,在Linux/Unix環境下,通常以下面格式表示locale名稱:language[_territory][.codeset][@modifier],language 為 ISO 639 中規定的語言程式碼,territory 為 ISO 3166 中規定的國家/地區程式碼,codeset 為字符集名稱。
在Linux下,可以使用 locale -a 命令檢視系統中所有已配置的 locale。用不帶選項的 locale 命令檢視當前 Shell 中活動的 locale。用 locale -m
命令檢視locale系統支援的所有可用的字符集編碼。和locale相關的包叫做:locales,locale系統支援的所有可用locale在檔案:/usr/share/i18n/SUPPORTED 中列出。
在Debian下,可用 dpkg-reconfigure locales 命令重新配置 locale,也可以手工修改 /etc/locale.gen 檔案,然後執行 locale-gen 命令。
在Ubuntu下,修改 /var/lib/locales/supported.d/local 檔案,配置新的 locale,然後執行 locale-gen 命令。
當 locale 為 NULL 時,函式只做取回當前 locale 操作,通過返回值傳出,並不改變當前 locale。
當 locale 為 "" 時,根據環境的設定來設定 locale,檢測順序是:環境變數 LC_ALL,每個單獨的locale分類LC_*,最後是 LANG 變數。為了使程式可以根據環境來改變活動 locale,一般都在程式的初始化階段加入下面程式碼:setlocale(LC_ALL, "")。
當C語言程式初始化時(剛進入到 main() 時),locale 被初始化為預設的 C locale,其採用的字元編碼是所有本地 ANSI 字符集編碼的公共部分,是用來書寫C語言源程式的最小字符集(所以才起locale名叫:C)。
當用 setlocale() 設定活動 locale 時,如果成功,會返回當前活動 locale 的全名稱;如果失敗,會返回 NULL。
Windows的CRT中的setlocale()
具體參考:setlocale - MSDN Run-Time Library Reference
在 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 編碼。
下面是我寫的一個使用 setlocale() 的示例:
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 字串。
http://www.cnblogs.com/hnrainll/archive/2011/05/07/2039700.html