C/C++代碼支持Unicode


C/C++代碼支持Unicode

這份文檔簡要的說明了如何修改你的C/C++代碼使之支持Unicode。在這里並不准備 解釋太多相關的技術細節並且我得假定你已經基本熟悉Microsoft支持Unicode的方式。 它的主要目的是方便你查詢相關的數據類型和函數,以及修正相應的拼寫錯誤。

I18nGuy 主頁  XenCraft (Unicode 咨詢公司)  English 

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。
  • 考慮遺留數據和文件的編碼慣例,考慮導入和導出以及傳輸協議。(MultiByteToWideCharWideCharToMultiBytembtowcwctombwctombsmbstowcs )
  • 考慮復制文本到剪貼板
    使用 CF_TEXT 格式並且寫入本地編碼的文本(ANSI格式)
    使用 CF_UNICODETEXT 格式並寫入Unicode文本
  • 數據庫程序需要考慮數據類型 (NCHAR, NVARCHAR) 和模式(schema)的變更,觸發器,存儲過程和查詢,數據的增長,索引以及性能等因素。
    注意:Unicode模式的改變對於不同廠商的數據庫產生不同的影響。如果需要數據庫具有較好的可移植性,就要考慮到每種數據庫的特性和行為。 
    (我知道針對這個問題說得不夠多,以后有機會再補充吧)

流式 I/O

如果你使用Microsoft C++編譯器,你可能遇到與流式 I/O相關的3類問題:

  1. 不支持Unicode文件名。
    解決方式是使用 FILE * _wfopen 函數,之后使用FILE句柄初始化流式 I/O。
    std::ifstream stm(_wfopen(pFilename, L"r"));
  2. 在 讀/寫 的時候,流式 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> >
  3. 如果要用流式 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高級技術,平台和其它因素

Unicode字節序標記(BOM) 值

編碼方式 BOM值
UTF-8 EF BB BF
UTF-16 
(big-endian)
FE FF
UTF-16 
(little-endian)
FF FE
UTF-16BE, UTF-32BE 
(big-endian)
No BOM!
UTF-16LE, UTF-32LE 
(little-endian)
No BOM!
UTF-32 
(big-endian)
00 00 FE FF
UTF-32 
(little-endian)
FF FE 00 00
SCSU 
(compression)
0E FE FF

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM