C語言的國際化

編寫的C語言程序可能在不同的國家或地區運行。不同的國家或地區有不同的語言、文化傳統(例如字符、數值顯示方式等),爲了讓C語言的程序能夠來自不同國家和地區的人使用,需要考慮C語言的國際化。

locale

locale是指根據計算機用戶使用的語言、所在的國家或地區以及文化傳統而定義的軟件運行時的語言環境。在Linux系統下打開Terminal,輸入locale命令,就可查看當前系統使用的語言環境。

使用的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。影響兩個字符串比較函數(strcollstrxfm)的行爲。
  • LC_CTYPE。影響<ctype.h>isdigitisxdigit以外函數的行爲。
  • LC_MONETARY。影響由localeconve函數返回的貨幣格式信息。
  • LC_NUMERIC。影響格式化輸入輸出函數使用的小數點字符以及stdlib.h中的字符串(atofstrtod)函數,及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_posnn_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;
}

locale

多字節字符和寬字符

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;
}

寬字符和多字節字符

注意:printfwprintf不能混合使用,因爲無論先使用哪一個,再使用後一個時行爲就是未定義的gcc 4.8.4版本不輸出任何東西。

三字符序列

爲了解決歐洲國家缺少#\]^{|字符的問題,C語言提供了三字符序列,作爲ASCII碼字符的替代。所有三字符序列都以??開始,其與ASCII碼的對應關係如下表:

三字符序列 ASCII碼
??= #
??(
??/ \
??) ]
??' ^
??< {
??! |
??>
??-

參考文獻

  1. K.N. King 著,呂秀峯 譯. C語言程序設計-現代方法. 人民郵電出版社
  2. http://stackoverflow.com/questions/8681623/printf-and-wprintf-in-single-c-code
  3. http://www.cplusplus.com/reference/clocale/setlocale/?kw=setlocale
  4. http://www.cnblogs.com/xlmeng1988/archive/2013/01/16/locale.html
  5. http://www.cnblogs.com/hnrainll/archive/2011/05/07/2039700.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章