優缺點:
ANSI碼 = 窄字符
Uincode = 寬字符
◆字符碼數據類型:
● Ansi: char、char * 、const char * C++
CHAR、(PCHAR、PSTR、LPSTR)、LPCSTR VC++
● Unicode: wchar_t、wchar_t * 、const wchar_t *
WCHAR、(PWCHAR、PWSTR、LPWSTR)、LPCWSTR
● T 通用類型: TCHAR、(TCHAR * 、PTCHAR、PTSTR、LPTSTR)、LPCTSTR
以上,其中:P代表指針的意思,STR代表字符串的意思,L是長指針的意思,在WIN32平台下可以忽略,C代表const常量的意思,W代表wide寬字節的意思,T大家可以理解為通用類型的意思,
通用類型是微軟為了方便使用定義的通用字符類型,在不同的編碼環境下,會根據是否定義了_UINCODE宏,自動會轉化為char 或 wchar_t;
◆ 字符串類型的對象的定義:
● Ansi:char *pAnsiStr = "hello";
● Unicode:wchar_t *pUnicodeStr = L"hello";
● 通用類型:TCHAR *pTStr = _T("hello"); 或者 TCHAR *pTStr = _TEXT("hello");
● 動態申請內存:TCHAR *pszBuf = new TCHAR[100]; //標示符很重要
其中,_TEXT 和 _T 是一樣的,定義如下:
#define _T(x) __T(x) #define _TEXT(x) __T(x) //來看看 __T 的最終定義: #ifdef _UNICODE #define __T(x) L##x //轉換Uincode #else #define __T(x) x //等於本身 #endif
其中,##為連接起來的意思。
◆ 常用的字符串處理函數,具體信息見MSDN:
字符串長度:
● Ansi:strlen(char *str); //獲取字符串長度,cs為cstring縮寫,len為length,w,_t字符串類型。方便記憶。也可以查詢MSDN
● Unicode:wcslen(wchar_t *str);
● 通用函數:_tcslen(TCHAR *str);
● Ansi:int atoi(const char *str); //轉換為數字,atoi,_wtoi,_tstoi.可以按照不同的顏色去記憶。分別為字符串類型+to+轉換類型。
● Unicode:int _wtoi(const wchar_t *str);
● 通用函數:_tstoi(const TCHAR *str);
字符串拷貝:
● Ansi:strcpy(char *strDestination, const char *strSource); //獲取字符串長度,cs為cstring縮寫,cpy為copy,w,_t字符串類型。方便記憶。也可以查詢MSDN
● Unicode:wcscpy(wchar_t *strDestination, const wchar_t *strSource);
● 通用函數:_tcscpy(TCHAR *strDestination, const TCHAR *strSource);
以上函數不安全,在vs2003等以上版本的編譯器中會有warnning警告提示,以下為安全函數(vc++6.0不支持):
● Ansi:strcpy_s(char *strDestination, size_t numberOfElements, const char *strSource);//_s可以理解為safe縮寫,保險的。
● Unicode:wcscpy_s(wchar_t *strDestination, size_t numberOfElements, const wchar_t *strSource);
● 通用函數:_tcscpy_s(TCHAR *strDestination, size_t numberOfElements, const TCHAR *strSource);
numberOfElements Size of the destination string buffer. 目的緩沖區的大小,以字節為單位,不是字符!
size_t unsigned integer,在MSDN中的解釋:Result of sizeof operator,也就是說 size_t 是 unsigned integer 即無符號整數。那為什麼會有size_t這個類型呢? 因為不同平台的操作系統(32/64)中 int/long 等類型所占的字節並不一樣,而 size_t 在不同的平台下有不同的定義。有點類似於TCHAR類型:
#ifndef _SIZE_T_DEFINED #ifdef _WIN64 typedef unsigned __int64 size_t; //8個字節,64位 #else typedef _W64 unsigned int size_t; //4個字節,32位 #endif #define _SIZE_T_DEFINED #endif
◆ 字符串占用字節數:
● Ansi:
char szStr[] = "abc";
占用字節數求法:sizeof(szStr);
char *psz = "defgh";
占用字節數求法:strlen(psz)*sizeof(char);
● Unicode:
wchar_t szwStr[] = L"abc";
占用字節數求法:sizeof(szwStr);
wchar_t *pwsz = L"defgh";
占用字節數求法:wcslen(pwsz)*sizeof(wchar_t);
● 通用函數:
TCHAR szStr[] = _T("abc");
占用字節數求法:sizeof(szStr);
TCHAR *psz = _T("defgh");
占用字節數求法:_tcslen(psz)*sizeof(TCHAR);
◆轉換用到的最根本的 API 函數:
WideCharToMultiByte 實現寬字節轉換到窄字節 //函數參數自行查詢MSDN。
MultiByteToWideChar 實現窄字節轉換到寬字節
WideCharToMultiByte 的代碼頁用來標記與新轉換的字符串相關的代碼頁;
MultiByteToWideChar 的代碼頁用來標記與一個多字節字符串相關的代碼頁,
[1]、常用的代碼頁有 CP_ACP 和 CP_UTF8 兩個: 使用 CP_ACP 代碼頁就實現了 ANSI 與 Unicode 之間的轉換;
使用 CP_UTF8 代碼頁就實現了 UTF-8 與 Unicode 之間的轉換。
[2]、dwFlags 參數允許我們進行額外的控制,但是,一般情況下都不使用這個標志,直接傳遞 0 就行了。
[3]、lpDefaultChar和pfUsedDefaultChar:
只有當WideCharToMultiByte函數遇到一個寬字節字符,而該字符在uCodePage參數標識的代碼頁中並沒有它的表示法時,WideCharToMultiByte函數才使用這兩個參數。如果寬字節字符不能被轉換,該函數便使用lpDefaultChar參數指向的字符。如果該參數是NULL(這是大多數情況下的參數值),那麼該函數使用系統的默認字符。該默認字符通常是個問號。這對於文件名來說是危險的,因為問號是個通配符。pfUsedDefaultChar參數指向一個布爾變量,如果Unicode字符串中至少有一個字符不能轉換成等價多字節字符,那麼函數就將該變量置為TRUE。如果所有字符均被成功地轉換,那麼該函數就將該變量置為FALSE。當函數返回以便檢查寬字節字符串是否被成功地轉換後,可以測試該變量。
● 兩個轉換函數的使用舉例:
char *cctryWideCharToAnsi(wchar_t *pWideChar) { if (!pWideChar) return NULL; char *pszBuf = NULL; int needBytes = WideCharToMultiByte(CP_ACP, 0, pWideChar, -1, NULL, 0, NULL, NULL); if (needBytes > 0){ pszBuf = new char[needBytes+1]; ZeroMemory(pszBuf, (needBytes+1)*sizeof(char)); WideCharToMultiByte(CP_ACP, 0, pWideChar, -1, pszBuf, needBytes, NULL, NULL); } return pszBuf; } wchar_t *cctryAnsiCharToWide(char *pChar) { if (!pChar) return NULL; wchar_t *pszBuf = NULL; int needWChar = MultiByteToWideChar(CP_ACP, 0, pChar, -1, NULL, 0); if (needWChar > 0){ pszBuf = new wchar_t[needWChar+1]; ZeroMemory(pszBuf, needWChar+1); MultiByteToWideChar(CP_ACP, 0, pChar, -1, pszBuf, needWChar); } return pszBuf; }
使用過後千萬別忘記釋放空間
◆宏轉換
A2W、W2A、T2A、T2W 宏的使用以及注意事項:
[1]、使用 alloca() 函數進行空間的申請,宏返回的地址空間是從棧上面申請的,那麼以後就不必釋放,這樣就涉及到了一個作用域的問題,具體見MSDN,
大家可以簡單的理解為“向下兼容”.
[2]、不要在一個函數的循環體中使用 A2W 等字符轉換宏,可能引起棧溢出。
比如:
#include <atlconv.h> void func() { while(true) { { USES_CONVERSION; testFunc(A2W("abc")); } } }