編寫的C語言程序可能在不同的國家或地區運行。不同的國家或地區有不同的語言、文化傳統(例如字符、數值顯示方式等),爲了讓C語言的程序能夠來自不同國家和地區的人使用,需要考慮C語言的國際化。
locale
locale
是指根據計算機用戶使用的語言、所在的國家或地區以及文化傳統而定義的軟件運行時的語言環境。在Linux
系統下打開Terminal
,輸入locale
命令,就可查看當前系統使用的語言環境。
可見,locale
主要包括以下12類內容:
類型 | 含義 |
---|---|
LC_TYPE |
語言符號及其分類 |
LC_NUMERIC |
數字 |
LC_TIME |
時間格式信息 |
LC_COLLATE |
比較和排序習慣 |
LC_MONETARY |
貨幣單位 |
LC_MESSAGES |
提示信息、錯誤信息、狀態信息、標題、標籤、按鈕和菜單等信息 |
LC_PAPER |
默認紙張尺寸大小 |
LC_NAME |
姓名書寫方式 |
LC_ADDRESS |
地址書寫方式 |
LC_TELEPHONE |
電話號碼書寫方式 |
LC_MEASUREMENT |
度量衡表達方式 |
LC_IDENTIFICATION |
對地域自身的描述信息 |
除了上述12項內容外,LANG
指定locale
未設置內容的默認值,大部分程序應用LANGUAGE
指定的語言作爲界面語言。LC_ALL
同時設置所有的內容,並且其優先級比每個內容單獨設置的優先級都高,而LANG
的優先級最低。
這12項內容的不同定義就形成不同的locale定義文件。Linux系統的locale定義文件在/usr/share/i18n/locales
:
locale
文件名稱的格式一般爲語言[_地域][.字符集][@修正值]
。例如de_DE.UTF-8@euro
是指語言爲德文,地域爲德國,字符集爲UTF-8
,且根據歐洲習慣進行了修正。
C語言的本地化
<locale.h>
中提供了setlocale
函數,爲不同的類型內容指定所屬的locale
,進而影響標準庫中一些函數的行爲,其原型爲:
char *setlocale(int category, const char *locale);
在C語言中第一個參數可選的類型及改變其所屬locale
對標準庫的行爲的影響分別爲:
LC_COLLATE
。影響兩個字符串比較函數(strcoll
和strxfm
)的行爲。LC_CTYPE
。影響<ctype.h>
除isdigit
和isxdigit
以外函數的行爲。LC_MONETARY
。影響由localeconve
函數返回的貨幣格式信息。LC_NUMERIC
。影響格式化輸入輸出函數使用的小數點字符以及stdlib.h
中的字符串(atof
和strtod
)函數,及localeconv
函數返回的非貨幣信息。LC_TIME
。影響strftime
函數的行爲。
第一個參數也可以是LC_ALL
,影響所有類型的行爲。
第二個參數locale
爲不同的locale
定義文件名。
當setlocale
調用成功,會返回一個指向與新locale
相關的字符串,如果調用失敗,則返回空指針。C
語言標準默認提供了兩種可能的locale
值,一種是"C"
,表示按正常方式執行,一種是""
,使用本地的默認語言環境。其他的不同的編譯器可能提供不同的類型,格式可能像前面提到的locale
定義文件名的格式。如果第二個參數傳入空值,那麼會返回一個與本地類型設置相關聯的字符串。在任何程序執行開始時,都會隱式調用setlocale(LC_ALL,"C")
。在程序運行過程中,可以使用setlocale(LC_ALL,"")
切換到本地語言環境。
<locale.h>
提供了localeconv
函數獲取當前locale
的有效說明信息。localeconv
函數返回一個struct lconv
類型結構的指針,該結構體包含當前locale
的詳細信息。此結構體具有靜態存儲權限。lconv
結構體類型中包含的變量爲:
類型 | 名稱 | 類型 | locale爲"c" 時的值 |
描述 |
---|---|---|---|---|
非貨幣類 | decimal_point |
char * |
"." |
十進制小數點字符 |
非貨幣類 | thousands_sep |
char * |
"" |
在十進制小數點前,用以分割數字組的字符 |
非貨幣類 | grouping |
char * |
"" |
數字組的大小尺寸 |
貨幣類 | int_curr_symbol |
char * |
"" |
國際貨幣符號 |
貨幣類 | currency_symbol |
char * |
"" |
區域貨幣符號 |
貨幣類 | mon_decimal_point |
char * |
"" |
十進制小數點符號 |
貨幣類 | mon_thousands_sep |
char * |
"" |
在十進制小數點前用以分割數值組的字符 |
貨幣類 | mon_grouping |
char * |
"" |
數字組的大小尺寸 |
貨幣類 | positive_sign |
char * |
"" |
用來說明非負值的字符串 |
貨幣類 | negative_sign |
char * |
"" |
用來說明負值的字符串 |
貨幣類 | int_frac_digits |
char |
CHAR_MAX |
十進制小數點後的數字個數(國際格式) |
貨幣類 | frac_digits |
char |
CHAR_MAX |
十進制小數點後的數字個數(區域格式) |
貨幣類 | p_cs_precedes |
char |
CHAR_MAX |
如果currency_symbol 先於非負值,則爲1,否則爲0 |
貨幣類 | p_sep_by_space |
char |
CHAR_MAX |
如果currency_symbol 使用空格來分割非負值,則爲1,否則爲0 |
貨幣類 | n_cs_precedes |
char |
CHAR_MAX |
如果currency_symbol 限於負值,則返回1,否則返回0 |
貨幣類 | n_sep_by_space |
char |
CHAR_MAX |
如果currency_symbol 使用空格來分割負值,則爲1,否則爲0 |
貨幣類 | p_sign_posn |
char |
CHAR_MAX |
表明positive_sign 的位置的非負值 |
貨幣類 | n_sign_posn |
char |
CHAR_MAX |
表明negative_sign 的位置的非負值 |
p_sign_posn
和n_sign_posn
具有不同的值具有以下含義:
值 | 含義 |
---|---|
0 | 圍繞在數量和區域貨幣符號周圍的圓括號 |
1 | 在數量和區域貨幣符號之前的符號 |
2 | 在數量和區域貨幣符號之後的符號 |
3 | 直接在區域貨幣符號之前的符號 |
4 | 直接在區域貨幣符號之後的符號 |
/*************************************
* using_locale.c *
* *
* C語言的國際化函數 *
* 案例來自http://www.cplusplus.com/reference/clocale/setlocale/?kw=setlocale *
*************************************/
#include <stdio.h>
#include <locale.h>
#include <time.h>
int main()
{
time_t rawtime;
struct tm *timeinfo;
char buffer[80];
struct lconv *lc;
time(&rawtime);
timeinfo = localtime(&rawtime);
int twice = 0;
do{
printf("locale is: %s\n", setlocale(LC_ALL, NULL));
strftime(buffer, 80, "%c", timeinfo);
printf("Date is: %s\n", buffer);
lc = localeconv();
printf("Currency symbol is : %s\n - \n", lc->currency_symbol);
setlocale(LC_ALL, "");
}while(!twice++);
return 0;
}
多字節字符和寬字符
char
型的大小往往只佔有一個字節,無法滿足不同國家和地區的龐大字符集的要求,C
語言允許編譯器提供擴展字符集,提供了兩種擴展字符集的編碼方式:多字節字符和寬字符,同時提供了將一種編碼轉換爲另一種編碼的函數。
多字節字符
在多字節字符中,一個或多個字節表示一個擴展字符。任何擴展字符集必須包括C語言要求的基本字符,並且要求是單字節的。這些字符是解釋多字節字符的基礎。C標準要求零字節始終表示空字符,並且不能作爲多字節字符的第二個字節。
C語言提供了兩種多字節字符相關的宏,一種是在<limits.h>
中定義的MB_LEN_MAX
,限定了在所有locale
下多字節字符可以佔用的最大字節值,而宏MB_CUR_MAX
指定了當前locale
下多字節字符佔用的最大字節值。
寬字符
寬字符是另外一種對字符集進行編碼的方式,寬字符編碼的擴展字符集中的所有的字符具有相同的字節數,使用其值表示字符。
寬字符的類型爲wchar_t
類型,定義在<stddef.h>
和<stdlib.h>
中。使用寬字符集可以利用C語言支持的寬字符常量和寬字符字面量,只需要在普通字符和字符串常量前加L
符號。
Unicode
是寬字符編碼的一個通用字符集,每個字符佔兩個字節。
多字節字符函數
C語言提供了以下幾個多字節字符函數:
int mblen(const char *s, size_t n);
int mbtowc(wchar_t *pwc, const char *s, size_t n);
int wctomb(char *s, wchar_t wchar);
mblen
函數檢測第一個參數指向的字節序列是否是有效的多字節字符的字節序列,如果是,則返回字符中的字節數,否則返回0。如果第一個參數爲空字符,則函數返回0. 第二個參數限制了檢測的字節數量,通常情況下會是MB_CUR_MAX
。
mbtowc
函數把第二個參數指向的多字節字符轉換爲寬字符。第一個參數指向函數將存儲結果的變量,第三個參數限制了mbtowc
函數將檢測的字節的數量,mbtowc
返回的值與mblen
相同。
wctomb
函數把寬字符轉換爲多字節字符,並把多字節字符存儲到第一個參數指向的數組中。如果有效,wctomb
函數將返回字符中字節的數量,否則,返回-1.
多字節字符串函數
C語言也提供了多字節字符串函數與寬字符串序列間轉換的函數,如下:
size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n);
size_t wcstombs(char *s, const wchar_t *s, size_t n);
mbstowcs
將多字節字符串轉換爲寬字符序列,第二個參數指向多字節字符串,第一個參數指向寬字符的數組,第三個參數限制了存儲在數組中的字符數量。當達到上限或遇到空字符時,則停止。函數返回修改的數組元素的數量。如果遇到無效的多字節字符,則返回-1.
wcstombs
將寬字符轉換爲多字節字符,函數的第二個參數指向寬字符串,第一個參數指向多字節字符的數組,第三個參數限制了存儲在數組中的字節的數量,當達到上限或遇到空字符時,則wcstombs
函數停止。函數返回存儲的字節數量,如果遇到一個寬字符無法對應任何多字節字符,則返回-1.
/**************************************
* using_mb_wc.c *
* *
* C語言中的寬字符和多字節字符 *
**************************************/
#include <stdlib.h>
#include <limits.h>
#include <locale.h>
#include <wchar.h>
int main()
{
setlocale(LC_ALL,"");
char mb[MB_LEN_MAX] = "";
wchar_t wc = L'\x3B1';
wctomb(NULL, 0);
int num = wctomb(mb, wc);
wprintf(L"需要%d個字節\n", num);
int i = 0;
for (; i<MB_CUR_MAX; i++)
wprintf(L"%x ", mb[i]);
mb[num] = '\0';
wprintf(L"\n");
if (mblen(mb, MB_CUR_MAX))
wprintf(L"mb是一個有效的多字節字符!");
wprintf(L"\n");
wchar_t wc2;
mbtowc(&wc2, mb, MB_CUR_MAX);
wprintf(L"wc2 %lc\n", wc2);
return 0;
}
注意:printf
和wprintf
不能混合使用,因爲無論先使用哪一個,再使用後一個時行爲就是未定義的,gcc 4.8.4
版本不輸出任何東西。
三字符序列
爲了解決歐洲國家缺少#
、[
、\
、]
、^
、{
、|
、}
和~
字符的問題,C語言提供了三字符序列,作爲ASCII碼字符的替代。所有三字符序列都以??
開始,其與ASCII碼的對應關係如下表:
三字符序列 | ASCII碼 |
---|---|
??= |
# |
??( |
[ |
??/ |
\ |
??) |
] |
??' |
^ |
??< |
{ |
??! |
| |
??> |
} |
??- |
~ |
參考文獻
- K.N. King 著,呂秀峯 譯. C語言程序設計-現代方法. 人民郵電出版社
- http://stackoverflow.com/questions/8681623/printf-and-wprintf-in-single-c-code
- http://www.cplusplus.com/reference/clocale/setlocale/?kw=setlocale
- http://www.cnblogs.com/xlmeng1988/archive/2013/01/16/locale.html
- http://www.cnblogs.com/hnrainll/archive/2011/05/07/2039700.html