C標準庫的setlocale()用法筆記

轉自:

http://zyxhome.org/wp/cc-prog-lang/c-stdlib-setlocale-usage-note/
http://www.cnblogs.com/hnrainll/archive/2011/05/07/2039700.html

[在此向原文作者說聲謝謝!若有讀者看到文章轉載時請寫該轉載地址,不要寫我的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() 的示例:

#ifdef __GNUC__

#define CSET_GBK    "GBK"
#define CSET_UTF8   "UTF-8"

#define LC_NAME_zh_CN   "zh_CN"

// ifdef __GNUC__
#elif defined(_MSC_VER)

#define CSET_GBK    "936"
#define CSET_UTF8   "65001"

#define LC_NAME_zh_CN   "Chinese_People's Republic of China"

// ifdef _MSC_VER
#endif

#define LC_NAME_zh_CN_GBK       LC_NAME_zh_CN "." CSET_GBK
#define LC_NAME_zh_CN_UTF8      LC_NAME_zh_CN "." CSET_UTF8
#define LC_NAME_zh_CN_DEFAULT   LC_NAME_zh_CN_GBK

void print_current_loc();

int main(int argc, char* argv[])
{
    char* locname = NULL;
    const wchar_t* strzh = L"中文字符串";

    print_current_loc();

     // 使用指定的 locale
    locname = setlocale(LC_ALL, LC_NAME_zh_CN_DEFAULT);
    if ( NULL == locname )
    {
        printf("setlocale() with %s failed.\n", LC_NAME_zh_CN_DEFAULT);
    }else{
        printf("setlocale() with %s succeed.\n", LC_NAME_zh_CN_DEFAULT);
    }

    print_current_loc();

    wprintf(L"Zhong text is: %ls\n", strzh);

    // 使用運行環境中的 locale 設置
    locname = setlocale(LC_ALL, "");
    if ( NULL == locname )
    {
        printf("setlocale() from environment failed.\n");
    }else{
       printf("setlocale() from environment succeed.\n");
    }
    print_current_loc();
    wprintf(L"Zhong text is: %ls\n", strzh);

    puts("End of program.");
    return 0;
}

    // 打印當前 locale
void print_current_loc()
{
    char* locname = setlocale(LC_ALL, NULL);
    printf("Current locale is: %s\n", locname);
}

要使上面程序成功編譯並執行,需要注意一下幾點:

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 字符串。
發佈了46 篇原創文章 · 獲贊 10 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章