C/C++代碼支持Unicode
這份文檔簡要的說明了如何修改你的C/C++代碼使之支持Unicode。在這里並不准備 解釋太多相關的技術細節並且我得假定你已經基本熟悉Microsoft支持Unicode的方式。 它的主要目的是方便你查詢相關的數據類型和函數,以及修正相應的拼寫錯誤。
My thanks to Yaker Gong for the translation to Chinese. Xie Xie!
使你的C/C++代碼支持Unicode的第一步
- 定義宏 _UNICODE, 如果定義了宏 _MBCS 則取消它的定義(undefine)。
- 在字符串前添加 L 標記或者用 _T宏修飾字符串。
- 使用 Wide 或者 TCHAR 版本的字符串處理函數。
- 確定API中的字符串長度是按字節計數還是按字符個數計數。因為基於字符的顯示和打印(與此不同的是,GUI是基於像素的)使用 列數,而不是字節數或者字符個數。
- 在字符串指針相關的計算中使用GetNext格式,因為一個字符可能包含多於一個Unicode字符單元。
- 注意緩沖區的大小以及防止緩沖區溢出。
改變編碼方式可能需要增大緩沖區或者限制字符串的最大長度。假設單個字符的大小從1個字節變為4個字節,並且字 符串本來20個字符占用20字節,那么你需要將字符串緩沖區擴大為80字節或者將字符串長度限制為5個字符(字符串緩沖區仍為20字節)。注意緩沖區的擴 大可能被限制到一個最大值(比如65KB)。減少字符串長度到一個固定值可能破壞現有的程序,限制字符串長度到固定值可能是危險的。比如,限制到20字 節,將字符串轉化為大寫形式就可能導致字符串變長並且超過限制。 - 將接受或者返回單字符參數的函數替換為使用字符串的版本。 (在一些語言中) 對於單個字符的操作可能導致返回多個代碼點。例如,upper('ß')將 返回"SS"。
- 使用 wmain 代替 main。環境變量也由_environ變 為_wenviron 。
wmain(int argc, wchar_t *argv[], wchar_t *envp[]) - MFC Unicode 程序使用 wWinMain 作為程序入口點(VC++ 6.0)。
project->settings->Link,選擇output選項卡
在Entry point symbol一行加上 wWinMainCRTStartup 。 - 字體的因素,分清使用的字體用於渲染各種語言的字符還是用於腳本中。
文件 I/O, 數據庫, 傳輸協議等因素
- 考慮是否需要讀寫文件、數據庫中的 UTF-8 或者 UTF-16 字符,以及是否進行數據交換。
- 考慮 UTF-16 格式文件中的字節序。
讀寫網絡傳輸的數據總是使用 Big-Endian ,如果你沒有產生 BOM 也使用 Big-Endian。
文件的字節序依賴於文件格式以及/或者源/目標機器的體系結構。
讀取 UTF-16 或者 UTF-32編碼的文件時,考慮是否需要將字符按字節逆序。
對於 streams 和傳輸協議也需要做上述的考慮。 - 傳輸協議和用於數據交換的文件要使用正確的編碼方式。例如 HTTP,HTML,XML 必須設置為 UTF-8 或者 UTF-16。
- 考慮Unicode字節序標記( BOM ,Byte Order Marker) 以及是否需要將它同數據一同寫入。讀取數據時記得去掉BOM。
- 考慮遺留數據和文件的編碼慣例,考慮導入和導出以及傳輸協議。(MultiByteToWideChar, WideCharToMultiByte, mbtowc, wctomb, wctombs, mbstowcs )
- 考慮復制文本到剪貼板
使用 CF_TEXT 格式並且寫入本地編碼的文本(ANSI格式)
使用 CF_UNICODETEXT 格式並寫入Unicode文本 - 數據庫程序需要考慮數據類型 (NCHAR, NVARCHAR) 和模式(schema)的變更,觸發器,存儲過程和查詢,數據的增長,索引以及性能等因素。
注意:Unicode模式的改變對於不同廠商的數據庫產生不同的影響。如果需要數據庫具有較好的可移植性,就要考慮到每種數據庫的特性和行為。
(我知道針對這個問題說得不夠多,以后有機會再補充吧)
流式 I/O
如果你使用Microsoft C++編譯器,你可能遇到與流式 I/O相關的3類問題:
- 不支持Unicode文件名。
解決方式是使用 FILE * _wfopen 函數,之后使用FILE句柄初始化流式 I/O。std::ifstream stm(_wfopen(pFilename, L"r")); - 在 讀/寫 的時候,流式 I/O 會把數據 從本地代碼頁(ANSI格式)轉換到Unicode格式/從Unicode格式轉換到ANSI格式,而非UTF-8 或者 UTF-16。
但是可以修改表示流的類使之支持讀寫 UTF-8 格式字符。你可以自己實現一個讀寫時把數據在 Unicode 和 UTF-8 之間轉換的I/O stream類。codecvt <wchar_t, char_traits <wchar_t> > - 如果要用流式 I/O讀寫 UTF-16 字符,應該用二進制模式打開並且在二進制模式下輸入輸出。可以用如下方法設置 I/O 為二進制模式:
_setmode( _fileno( stdin ), _O_BINARY );
也可以參考 Microsoft 運行時庫參考: "Unicode Stream I/O in Text and Binary Modes".
注意:針對cout/wcout, cin/wcin等並沒有相應的 TCHAR 版本。如果你需要在ANSI/Unicode兩種模式下編譯 代碼,你可能需要自己定義一個名字類似"tout"的宏。
國際化,Unicode高級技術,平台和其它因素
- 考慮使用基於地區的辦法和更進一步的國際化。
- 對於 Windows 95,98 和 Windiws ME,考慮使用 Microsoft MSLU (Microsoft Layer for Unicode)
- 考慮字符串比較和排序,Unicode Collation Algorithm
- 考慮 Unicode Normalization
- 考慮 Character Folding
重新 考慮是否要自己處理這些事情。借助於一家Unicode咨詢公司,然后讓你的團隊集中精力做他們擅長的事情。(嗨, 我們也要謀生啊...)
Unicode字節序標記(BOM) 值
|
Unicode字節序標記(BOM,Byte Order Marker) 是Unicode字符 U+FEFF。(它也能表示一個被稱作 Zero Width No-break Space 的字符)。U+FFFE 這個代碼點在Unicode中是非法的,它永遠不應該出現在一個Unicode字符流中。所以BOM可以作為放置於文件(或者一個字符串)的起始作為字節 序的指示器。對UTF-16編碼而言,如果第一個字符的值是FE FF 那么文本和讀取文本的機器有相同的字節序。如果是 FF FE,那么有相反的字節序並且需要對每 個16-bit字按字節逆序。同樣的,BOM指示了UTF-32編碼的文本的字節序。 注意不是所有的文件都以Unicode字節序標記開始。事實上,Unicode標准稱若不以Unicode字節序標記 (BOM)開始(數據)就必須被表示成big-endian形式。 字符 U+FEFF 同樣作為不同Unicode編碼方式的標記。左邊的表格說明了 U+FEFF 在每一種Unicode編碼方式中的值。注意:按照定義,標記為UTF-16BE, UTF-32BE, UTF-32LE or UTF-16LE 的文本不應該有BOM,字節序已經由標記本身指出了。 對於使用 SCSU (Standard Compression Scheme for Unicode) 算法壓縮過的文本,也有一個推薦的簽名。 |
常量和全局變量
ANSI版本 | 寬字符版本 | 宏定義版本(TCHAR) |
---|---|---|
EOF | WEOF | _TEOF |
_environ | _wenviron | _tenviron |
_pgmptr | _wpgmptr | _tpgmptr |
數據類型
ANSI版本 | 寬字符版本 | 宏定義版本(TCHAR) |
---|---|---|
char | wchar_t | _TCHAR |
_finddata_t | _wfinddata_t | _tfinddata_t |
__finddata64_t | __wfinddata64_t | _tfinddata64_t |
_finddatai64_t | _wfinddatai64_t | _tfinddatai64_t |
int | wint_t | _TINT |
signed char | wchar_t | _TSCHAR |
unsigned char | wchar_t | _TUCHAR |
char | wchar_t | _TXCHAR |
L | _T 或者 _TEXT | |
LPSTR (char *) |
LPWSTR (wchar_t *) |
LPTSTR (_TCHAR *) |
LPCSTR (const char *) |
LPCWSTR (const wchar_t *) |
LPCTSTR (const _TCHAR *) |
LPOLESTR (For OLE) |
LPWSTR | LPTSTR |
Platform SDK字符串處理API
有很多Windows API函數會根據宏 UNICODE 是 否被定義而編譯成不同形式。那些需要同時操作ANSI字符和寬字符的模塊需要了解這一點。否則,應該使用宏定義版本的名字,這樣的話就只需要定義宏 UNICODE 並且重新編譯程序。
下列列表並沒有列舉所有的有ANSI和寬字符兩個版本的API,只列舉了與字符和字符串處理相關的一些。如果需要查看與代碼頁和地區相 關 的API請查看WinNLS.h頭文件。
ANSI版本 | 寬字符版本 | 宏定義版本(TCHAR) |
---|---|---|
CharLowerA | CharLowerW | CharLower |
CharLowerBuffA | CharLowerBuffW | CharLowerBuff |
CharNextA | CharNextW | CharNext |
CharNextExA | CharNextExW | CharNextEx |
CharPrevA | CharPrevW | CharPrev |
CharPrevExA | CharPrevExW | CharPrevEx |
CharToOemA | CharToOemW | CharToOem |
CharToOemBuffA | CharToOemBuffW | CharToOemBuff |
CharUpperA | CharUpperW | CharUpper |
CharUpperBuffA | CharUpperBuffW | CharUpperBuff |
CompareStringA | CompareStringW | CompareString |
FoldStringA | FoldStringW | FoldString |
GetStringTypeA | GetStringTypeW | GetStringType |
GetStringTypeExA | GetStringTypeExW | GetStringTypeEx |
IsCharAlphaA | IsCharAlphaW | IsCharAlpha |
IsCharAlphaNumericA | IsCharAlphaNumericW | IsCharAlphaNumeric |
IsCharLowerA | IsCharLowerW | IsCharLower |
IsCharUpperA | IsCharUpperW | IsCharUpper |
LoadStringA | LoadStringW | LoadString |
lstrcatA | lstrcatW | lstrcat |
lstrcmpA | lstrcmpW | lstrcmp |
lstrcmpiA | lstrcmpiW | lstrcmpi |
lstrcpyA | lstrcpyW | lstrcpy |
lstrcpynA | lstrcpynW | lstrcpyn |
lstrlenA | lstrlenW | lstrlen |
OemToCharA | OemToCharW | OemToChar |
OemToCharBuffA | OemToCharBuffW | OemToCharBuff |
wsprintfA | wsprintfW | wsprintf |
wvsprintfA | wvsprintfW | wvsprintf |
CRT字符串處理API
函數按照ANSI版本的ASCII字母順序排序,方便轉換到相應的Unicode版本。
ANSI版本 | 寬字符版本 | 宏定義版本(TCHAR) |
---|---|---|
_access | _waccess | _taccess |
_atoi64 | _wtoi64 | _tstoi64 |
_atoi64 | _wtoi64 | _ttoi64 |
_cgets | _cgetws | cgetts |
_chdir | _wchdir | _tchdir |
_chmod | _wchmod | _tchmod |
_cprintf | _cwprintf | _tcprintf |
_cputs | _cputws | _cputts |
_creat | _wcreat | _tcreat |
_cscanf | _cwscanf | _tcscanf |
_ctime64 | _wctime64 | _tctime64 |
_execl | _wexecl | _texecl |
_execle | _wexecle | _texecle |
_execlp | _wexeclp | _texeclp |
_execlpe | _wexeclpe | _texeclpe |
_execv | _wexecv | _texecv |
_execve | _wexecve | _texecve |
_execvp | _wexecvp | _texecvp |
_execvpe | _wexecvpe | _texecvpe |
_fdopen | _wfdopen | _tfdopen |
_fgetchar | _fgetwchar | _fgettchar |
_findfirst | _wfindfirst | _tfindfirst |
_findnext64 | _wfindnext64 | _tfindnext64 |
_findnext | _wfindnext | _tfindnext |
_findnexti64 | _wfindnexti64 | _tfindnexti64 |
_fputchar | _fputwchar | _fputtchar |
_fsopen | _wfsopen | _tfsopen |
_fullpath | _wfullpath | _tfullpath |
_getch | _getwch | _gettch |
_getche | _getwche | _gettche |
_getcwd | _wgetcwd | _tgetcwd |
_getdcwd | _wgetdcwd | _tgetdcwd |
_ltoa | _ltow | _ltot |
_makepath | _wmakepath | _tmakepath |
_mkdir | _wmkdir | _tmkdir |
_mktemp | _wmktemp | _tmktemp |
_open | _wopen | _topen |
_popen | _wpopen | _tpopen |
_putch | _putwch | _puttch |
_putenv | _wputenv | _tputenv |
_rmdir | _wrmdir | _trmdir |
_scprintf | _scwprintf | _sctprintf |
_searchenv | _wsearchenv | _tsearchenv |
_snprintf | _snwprintf | _sntprintf |
_snscanf | _snwscanf | _sntscanf |
_sopen | _wsopen | _tsopen |
_spawnl | _wspawnl | _tspawnl |
_spawnle | _wspawnle | _tspawnle |
_spawnlp | _wspawnlp | _tspawnlp |
_spawnlpe | _wspawnlpe | _tspawnlpe |
_spawnv | _wspawnv | _tspawnv |
_spawnve | _wspawnve | _tspawnve |
_spawnvp | _wspawnvp | _tspawnvp |
_spawnvpe | _wspawnvpe | _tspawnvpe |
_splitpath | _wsplitpath | _tsplitpath |
_stat64 | _wstat64 | _tstat64 |
_stat | _wstat | _tstat |
_stati64 | _wstati64 | _tstati64 |
_strdate | _wstrdate | _tstrdate |
_strdec | _wcsdec | _tcsdec |
_strdup | _wcsdup | _tcsdup |
_stricmp | _wcsicmp | _tcsicmp |
_stricoll | _wcsicoll | _tcsicoll |
_strinc | _wcsinc | _tcsinc |
_strlwr | _wcslwr | _tcslwr |
_strncnt | _wcsncnt | _tcsnbcnt |
_strncnt | _wcsncnt | _tcsnccnt |
_strncnt | _wcsncnt | _tcsnccnt |
_strncoll | _wcsncoll | _tcsnccoll |
_strnextc | _wcsnextc | _tcsnextc |
_strnicmp | _wcsnicmp | _tcsncicmp |
_strnicmp | _wcsnicmp | _tcsnicmp |
_strnicoll | _wcsnicoll | _tcsncicoll |
_strnicoll | _wcsnicoll | _tcsnicoll |
_strninc | _wcsninc | _tcsninc |
_strnset | _wcsnset | _tcsncset |
_strnset | _wcsnset | _tcsnset |
_strrev | _wcsrev | _tcsrev |
_strset | _wcsset | _tcsset |
_strspnp | _wcsspnp | _tcsspnp |
_strtime | _wstrtime | _tstrtime |
_strtoi64 | _wcstoi64 | _tcstoi64 |
_strtoui64 | _wcstoui64 | _tcstoui64 |
_strupr | _wcsupr | _tcsupr |
_tempnam | _wtempnam | _ttempnam |
_ui64toa | _ui64tow | _ui64tot |
_ultoa | _ultow | _ultot |
_ungetch | _ungetwch | _ungettch |
_unlink | _wunlink | _tunlink |
_utime64 | _wutime64 | _tutime64 |
_utime | _wutime | _tutime |
_vscprintf | _vscwprintf | _vsctprintf |
_vsnprintf | _vsnwprintf | _vsntprintf |
asctime | _wasctime | _tasctime |
atof | _wtof | _tstof |
atoi | _wtoi | _tstoi |
atoi | _wtoi | _ttoi |
atol | _wtol | _tstol |
atol | _wtol | _ttol |
字符比較 | 映射為宏或者inline 函數 | _tccmp |
字符拷貝 | 映射為宏或者inline 函數 | _tccpy |
字符長度 | 映射為宏或者inline 函數 | _tclen |
ctime | _wctime | _tctime |
fgetc | fgetwc | _fgettc |
fgets | fgetws | _fgetts |
fopen | _wfopen | _tfopen |
fprintf | fwprintf | _ftprintf |
fputc | fputwc | _fputtc |
fputs | fputws | _fputts |
freopen | _wfreopen | _tfreopen |
fscanf | fwscanf | _ftscanf |
getc | getwc | _gettc |
getchar | getwchar | _gettchar |
getenv | _wgetenv | _tgetenv |
gets | getws | _getts |
isalnum | iswalnum | _istalnum |
isalpha | iswalpha | _istalpha |
isascii | iswascii | _istascii |
iscntrl | iswcntrl | _istcntrl |
isdigit | iswdigit | _istdigit |
isgraph | iswgraph | _istgraph |
islead (總是返回FALSE) | (總是返回FALSE) | _istlead |
isleadbyte (總是返回FALSE) | isleadbyte (總是返回FALSE) | _istleadbyte |
islegal (總是返回TRUE) | (總是返回TRUE) | _istlegal |
islower | iswlower | _istlower |
isprint | iswprint | _istprint |
ispunct | iswpunct | _istpunct |
isspace | iswspace | _istspace |
isupper | iswupper | _istupper |
isxdigit | iswxdigit | _istxdigit |
main | wmain | _tmain |
perror | _wperror | _tperror |
printf | wprintf | _tprintf |
putc | putwc | _puttc |
putchar | putwchar | _puttchar |
puts | _putws | _putts |
remove | _wremove | _tremove |
rename | _wrename | _trename |
scanf | wscanf | _tscanf |
setlocale | _wsetlocale | _tsetlocale |
sprintf | swprintf | _stprintf |
sscanf | swscanf | _stscanf |
strcat | wcscat | _tcscat |
strchr | wcschr | _tcschr |
strcmp | wcscmp | _tcscmp |
strcoll | wcscoll | _tcscoll |
strcpy | wcscpy | _tcscpy |
strcspn | wcscspn | _tcscspn |
strerror | _wcserror | _tcserror |
strftime | wcsftime | _tcsftime |
strlen | wcslen | _tcsclen |
strlen | wcslen | _tcslen |
strncat | wcsncat | _tcsncat |
strncat | wcsncat | _tcsnccat |
strncmp | wcsncmp | _tcsnccmp |
strncmp | wcsncmp | _tcsncmp |
strncpy | wcsncpy | _tcsnccpy |
strncpy | wcsncpy | _tcsncpy |
strpbrk | wcspbrk | _tcspbrk |
strrchr | wcsrchr | _tcsrchr |
strspn | wcsspn | _tcsspn |
strstr | wcsstr | _tcsstr |
strtod | wcstod | _tcstod |
strtok | wcstok | _tcstok |
strtol | wcstol | _tcstol |
strtoul | wcstoul | _tcstoul |
strxfrm | wcsxfrm | _tcsxfrm |
system | _wsystem | _tsystem |
tmpnam | _wtmpnam | _ttmpnam |
tolower | towlower | _totlower |
toupper | towupper | _totupper |
ungetc | ungetwc | _ungettc |
vfprintf | vfwprintf | _vftprintf |
vprintf | vwprintf | _vtprintf |
vsprintf | vswprintf | _vstprintf |
WinMain | wWinMain | _tWinMain |