【字符集問題】一文看懂SBCS、MBCS、Unicode、TCHAR和相關函數

單字節和多字節字符集:SBCS vs MBCS

單字節字符集(SBCS)

  • ASCII字符集定義的是0x00 - 0x7F範圍內的字符。
    • 還有許多其他字符集,主要是歐洲字符集,它們在與0x字符集相同的範圍內定義0x00 - 0x7F範圍內的字符,並且還定義了0x80 - 0xFF的擴展字符集。
  • 因此,一個8位的單字節字符集(SBCS)足以表示ASCII字符集以及許多歐洲語言的字符集。但是,一些非歐洲字符集(如日語漢字)包含的字符數多於單字節編碼方案中可以表示的字符數,因此需要多字節字符集(MBCS)編碼。

多字節字符集(MBCS)

  • 多字節字符集可以包含單字節和雙字節字符。因此,多字節字符串可以包含單字節和雙字節字符的混合。
  • 雙字節多字節字符具有前導字節和跟蹤字節。在特定的多字節字符集中,前導字節落在特定範圍內,跟蹤字節也是如此。當這些範圍重疊時,可能需要評估特定上下文以確定給定字節是作爲前導字節還是跟蹤字節。

SBCS和MBCS的數據類型

處理字符串上下文中的多字節字節或字符的MBCS要求將多字節字符串表示爲unsigned char指針。
多字節字符的每個字節可以用8位字符表示。但是,類型爲char且值大於0x7F 的SBCS或MBCS單字節字符爲負數。當這樣的字符直接轉換爲int或long時,結果會被編譯器進行符號擴展,因此會產生意外的結果。

  • 最好將多字節字符的字節表示爲8位unsigned char,以避免負的結果。
    由於某些SBCS字符串處理函數採用(帶符號)char *參數,因此在定義_MBCS時將產生類型不匹配編譯器警告。
    有三種方法可以避免此警告,按效率順序列出:
  1. 在TCHAR.H中使用類型安全的內聯函數。這是默認行爲。
  2. 通過在命令行上定義_MB_MAP_DIRECT,在TCHAR.H中使用直接宏。如果執行此操作,則必須手動匹配類型。這是最快的方法,但不是類型安全的。
  3. 在TCHAR.H中使用類型安全的靜態鏈接庫函數。爲此,請在命令行上定義常量_NO_INLINING。這是最慢的方法,但是最安全的類型。

Unicode:寬字符集

  • Unicode 是 16 位字符編碼、 爲適用於所有語言提供足夠的編碼。 所有的 ASCII 字符以 unicode 格式作爲加寬字符包含在內。
  • 寬字符是2字節多語言字符代碼。全世界現代計算中使用的任何字符,包括技術符號和特殊發佈字符,都可以根據Unicode規範表示爲寬字符。由包括Microsoft在內的大型聯盟開發和維護,Unicode標準現已被廣泛接受。
  • 支持多字節字符集 (MBCS) 的窗體在所有平臺上稱爲雙字節字符集 (DBCS)。
    DBCS 字符由 1 或 2 個字節構成。 某些範圍的字節將留出使用用作前導字節。 前導字節指定它和以下結尾字節構成單個的 2 個字節寬字符。 在特定的多字節字符集中,前導字節位於某個範圍內,尾字節也是如此。 這些範圍重疊時,可能需要計算上下文以確定某個給定的字節用作前導字節還是尾字節。

寬字符的類型爲wchar_t。寬字符串表示爲wchar_t []數組,並由wchar_t*指針指向。
可以通過在字符前添加L前綴來將任何ASCII字符表示爲寬字符。例如,L’\ 0’是終止符的寬字符(16位)。類似地,也可以將任何ASCII字符串表示爲寬字符串,只需在ASCII字符串前加上字母L(L“Hello”)。

通常,寬字符在內存中佔用的空間比多字節字符多,但處理速度更快。此外,在多字節編碼中一次只能表示一個語言環境,而世界上的所有字符集都由Unicode表示同時表示。

TCHAR.H中的通用文本映射

爲簡化各種國際市場的代碼開發,Microsoft運行時庫爲許多數據類型和對象提供Microsoft特定的“通用文本”映射。這些映射在TCHAR.H中定義
可以使用這些名稱映射來編寫ASCII(SBCS),MBCS或Unicode三種字符集中任何一種的通用代碼,具體取決於使用#define語句定義的常量。通用文本映射是不兼容ANSI的Microsoft擴展。

通用文本映射的預處理程序指令

#define 編譯版
_UNICODE Unicode(寬字符) _tcsrev 映射到 _wcsrev
_MBCS 多字節字符 _tcsrev 映射到 _mbsrev
無(默認值:既未定義_UNICODE,也未定義_MBCS) SBCS(ASCII) _tcsrev 映射到 strrev

例如,通用的文本功能_tcsrev,在TCHAR.H定義,如果MBCS已被定義則映射到mbsrev,如果_UNICODE已被定義則映射到_wcsrev,否則_tcsrev映射到strrev。

通用文本數據類型的映射

通用文本數據類型名稱 SBCS(_UNICODE,_MBCS未定義) _MBCS已定義 _UNICODE已定義
_TCHAR char char wchar_t
_TINT int int wint_t
_TSCHAR signed char signed char wchar_t
_TUCHAR unsigned char unsigned char wchar_t
_TXCHAR char unsigned char wchar_t
_T 或 _TEXT 沒有效果 沒有效果 L (將後續字符或字符串轉換爲Unicode項)

TCHAR映射舉例

以下代碼片段說明了使用_TCHAR和_tcsrev映射到MBCS,Unicode和SBCS模型。

_TCHAR *RetVal, *szString;
RetVal = _tcsrev(szString);

如果MBCS已定義,則預處理器將前面的代碼映射到以下代碼:

char *RetVal, *szString;
RetVal = _mbsrev(szString);

如果_UNICODE已定義,則預處理器將相同的代碼映射到以下代碼:

wchar_t *RetVal, *szString;
RetVal = _wcsrev(szString);

如果既未定義_MBCS,也未定義_UNICODE,則預處理器將代碼映射到單字節ASCII代碼,如下所示:

char *RetVal, *szString;
RetVal = strrev(szString);

因此,可以編寫、維護和編譯統一的源代碼文件,實現三種字符集下的運行。

字符處理函數

常規 使用 setlocale類別設置依賴
atof,_atof_l,_wtof,_wtof_l 將字符轉換爲浮點值 LC_NUMERIC
atoi,_atoi_l,_wtoi,_wtoi_l 將字符轉換爲整數值 LC_NUMERIC
_atoi64,_atoi64_l,_wtoi64,_wtoi64_l 將字符轉換爲64位整數值 LC_NUMERIC
atol,_atol_l,_wtol,_wtol_l 將字符轉換爲長值 LC_NUMERIC
_atodbl,_atodbl_l,_atoldbl,_atoldbl_l,_atoflt,_atoflt_l 將字符轉換爲double-long值 LC_NUMERIC
是常規 針對特定條件測試給定整數。 LC_CTYPE
isleadbyte,_isleadbyte_l 測試引導字節 LC_CTYPE
localeconv 讀取用於格式化數字量的適當值 LC_MONETARY, LC_NUMERIC
MB_CUR_MAX 當前語言環境中任何多字節字符的最大長度(以字節爲單位)(在STDLIB.H中定義的宏) LC_CTYPE
_mbccpy,_mbccpy_l,_mbccpy_s,_mbccpy_s_l 複製一個多字節字符 LC_CTYPE
_mbclen,mblen,_mblen_l 驗證並返回多字節字符的字節數 LC_CTYPE
strlen,wcslen,_mbslen,_mbslen_l,_mbstrlen,_mbstrlen_l 對於多字節字符串:驗證字符串中的每個字符; 返回字符串長度 LC_CTYPE
mbstowcs,_mbstowcs_l,mbstowcs_s,_mbstowcs_s_l 將多字節字符序列轉換爲相應的寬字符序列 LC_CTYPE
mbtowc,_mbtowc_l 將多字節字符轉換爲相應的寬字符 LC_CTYPE
printf函數 寫格式化輸出 LC_NUMERIC(確定基數字符輸出)
scanf功能 讀取格式化輸入 LC_NUMERIC(確定基數字符識別)
setlocale,_wsetlocale 選擇程序的區域設置 不適用
strcoll,wcscoll,_mbscoll,_strcoll_l,_wcscoll_l,_mbscoll_l 比較兩個字符串的字符 LC_COLLATE
_stricmp,_wcsicmp,_mbsicmp,_stricmp_l,_wcsicmp_l,_mbsicmp_l 比較兩個字符串而不考慮大小寫 LC_CTYPE
_stricoll,_wcsicoll,_mbsicoll,_stricoll_l,_wcsicoll_l,_mbsicoll_l 比較兩個字符串的字符(不區分大小寫) LC_COLLATE
_strncoll,_wcsncoll,_mbsncoll,_strncoll_l,_wcsncoll_l,_mbsncoll_l 比較兩個字符串的前n個字符 LC_COLLATE
_strnicmp,_wcsnicmp,_mbsnicmp,_strnicmp_l,_wcsnicmp_l,_mbsnicmp_l 比較兩個字符串的字符而不考慮大小寫。 LC_CTYPE
_strnicoll,_wcsnicoll,_mbsnicoll,_strnicoll_l,_wcsnicoll_l,_mbsnicoll_l 比較兩個字符串的前n個字符(不區分大小寫) LC_COLLATE
strftime,wcsftime,_strftime_l,_wcsftime_l 根據提供的格式參數格式化日期和時間值 LC_TIME
_strlwr,_wcslwr,_mbslwr,_strlwr_l,_wcslwr_l,_mbslwr_l,_strlwr_s,_strlwr_s_l,_mbslwr_s,_mbslwr_s_l,_wcslwr_s,_wcslwr_s_l 將給定字符串中的每個大寫字母轉換爲小寫 LC_CTYPE
strtod,_strtod_l,wcstod,_wcstod_l 將字符串轉換爲double值 LC_NUMERIC(確定基數字符識別)
strtol,wcstol,_strtol_l,_wcstol_l 將字符串轉換爲長值 LC_NUMERIC(確定基數字符識別)
strtoul,_strtoul_l,wcstoul,_wcstoul_l 將字符串轉換爲無符號長值 LC_NUMERIC(確定基數字符識別)
_strupr,_strupr_l,_mbsupr,_mbsupr_l,_wcsupr_l,_wcsupr,_strupr_s,_strupr_s_l,_mbsupr_s,_mbsupr_s_l,_wcsupr_s,_wcsupr_s_l 將字符串中的每個小寫字母轉換爲大寫 LC_CTYPE
strxfrm,wcsxfrm,_strxfrm_l,_wcsxfrm_l 根據區域設置將字符串轉換爲整理形式 LC_COLLATE
tolower,_tolower,towlower,_tolower_l,_towlower_l,_mbctolower,_mbctolower_l,_mbctoupper,_mbctoupper_l 將給定字符轉換爲相應的小寫字符 LC_CTYPE
toupper,_toupper,towupper,_toupper_l,_towupper_l,_mbctolower,_mbctolower_l,_mbctoupper,_mbctoupper_l 將給定字符轉換爲相應的大寫字母 LC_CTYPE
wcstombs,_wcstombs_l,wcstombs_s,_wcstombs_s_l 將寬字符序列轉換爲多字節字符的對應序列 LC_CTYPE
wctomb,_wctomb_l,wctomb_s,_wctomb_s_l 將寬字符轉換爲相應的多字節字符 LC_CTYPE

注意:

對於多字節代碼,多字節代碼頁必須等同於使用setlocale設置的語言環境。_setmbcp,參數爲_MB_CP_LOCALE,使多字節代碼頁與setlocale代碼頁相同。

參考鏈接

https://docs.microsoft.com/en-us/cpp/c-runtime-library/internationalization?view=vs-2019

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章