最近一直在做基於Windows的開發,而windows的開發當中最讓我迷惑的一個問題就是無窮無盡的字符串類型,char*, wchar *, lpstr, lptstr, lpctstr等等,然後還要經常涉及到其中的相互轉換,所以想系統的研究一下所有的windows類型及其相互轉換的關系。
1.字符類型
講到字符串,首先首先需要了解的是字符,在Windows中主要包含兩種字符,一種是ANSI字符,另一種是Unicode字符使用UTF16編碼,即每個字符編碼為2個字節)。C++中表示ANSI字符的關鍵字是char,然後每個字符的長度是1;而表示Unicode字符的類型是wchar_t,每個字符的長度是2。而為了和語言有一些區別,Windows又定義了自己的數據類型這是微軟最煩的一點了!)
typedef charCHAR; //8位字符
typedefwchar_t WCHAR;//16位字符
2.字符串
講完了字符類型,現在開始講字符串了,Windows定義了非常多的字符指針或字符串指針
// 8位字符指針
typedef CHAR*PCHAR;
typedef CHAR*PSTR;
typedef CONSTCHAR *PCSTR
// 16位字符指針
typedef WCHAR*PWCHAR;
typedef WCHAR*PWSTR;
typedef CONSTWCHAR *PCWSTR;
首先介紹的是上面定義的六種字符指針,名字還是比較好記的,P代表Point指針),W代碼Unicode,C代表Const常量),CHAR 和STR代碼字符。所以PCHAR和PSTR的意思是一樣的就是ANSI類型的字符指針;而PWCHAR和PWSTR的類型也是一樣的,就是Unicode類型的字符指針;PCSTR和PCWSTR分別代表常量ANSI字符指針和常量Unicode字符指針什麼?不知道啥叫常量?請出門左轉《EffectiveC++》條款03,盡可能使用Const)。
同時,Windows為了保證使用ANSI或Unicode都能通過編譯,又定義了下面的類型,這些宏會自動檢測是否定義了Unicode。
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST WCHAR *PCTSTR;
#define __TEXT(quote) quote // r_winnt
#define __TEXT(quote) L##quote
#else
typedef CHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST CHAR *PCTSTR;
#define __TEXT(quote) quote
#endif
#defineTEXT(quote) __TEXT(quote)
從上面可以看出,如果定義了UNICODE宏,TCHAR等同於WCHAR, PTSTR和PTCHAR等同於PWCHAR和PWSTR,而如果沒有定義UNICODE宏,則TCHAR等同於CHAR,PTSTR和PTCHAR等同於PCHAR和PSTR;PCTSTR也是同理。
同時還要注意的是,這裡添加了TEXT宏,TEXT等同於__TEXT宏,__TEXT則能自動將字符串轉換為ANSI類型字符串或UNICODE類型字符串。而L加字符串則自動表示為Unicode類型字符串。
而在很多場合還會出現LPSTR,LPCHAR, LPCSTR,這個L是對應於16位OS而已代表long,也就是長指針,而現在32位的OS上所有前面加L的類型和不加L的類型已經沒有任何區別了。
下一種類型是BSTR, BSTR是一個Pascal-Style字符串和C-Style字符串的混合物,在COM中用的非常多,因為COM接口希望字符串可以用在所有語言中,繼而定義了這個類型。Pascal-style字符串是在存儲字符串的首幾個字節會用在記錄字符串的長度,所以這種類型的字符串不需要在末尾添加額外的字符標示結束。所以BSTR字符串的意思是在字符串的首幾個字節存儲字符串的長度,而字符串的結尾以\0結束。但是對BSTR字符串取下標時則是指向第一個字符而不是字符串長度,這樣又利用C-Style的程序使用。
然後還有兩個標准的字符串類, String和CString。String是標准C++中提供的程序庫,而CString則是MFC中提供的字符串類。
好吧,我們最後來總結一下所有的字符串類型。
按照不同的style字符類型分有五類:
C-Style ANSI類型字符串:CHAR *,PSTR, LPSTR, LPCSTR, PCHAR.
C-StyleUnicode類型字符串:WCHAR *, PWSTR, LPWSTR, PWCHAR,LPCWSTR.
C-Style 同時兼容ANSI和Unicode類型字符串:TEXT,__TEXT(), TCHAR *, PTSTR, LPTSTR, PCTSTR, LPCTSTR.
C-style和Pascal-Style兼並的字符串:BSTR
兩種C++類:String, CString.
不同類型字符串組合的含義技巧是:
L (無效) + P(字符指針) + 不加(ANSI類型)或W(Unicode類型) 或T (ANSI 或Unicode類型) + 不加(非常量字符串)或C(常量字符串) + STR或CHAR(CHAR結尾時前面不能加C).
3.轉化
下面是重頭戲,也就是不同類型直接的轉換技巧:
轉換的順序是:
1)首先先講可以等號直接轉換的:
CHAR * ==PSTR, LPSTR
WCHAR * ==PWSTR, LPWSTR
TCHAR * ==PTSTR, LPTSTR
原因也很明顯,其實都是一種類型嘛。
同時CHAR *類型和WCHAR *也能同時轉換為TCHAR *,反之不行。
而PSTR可以直接等於轉換PCSTR,PWSTR等於轉換為PWCSTR,反之則不行。
2)PCSTR轉換為PSTR
一般情況下可以使用C語言方式的強制類型轉換,或者C++方式的Const_cast將const去掉。
3)PSTR轉換為PWSTR
一般有兩種方式,第一種是簡單的W2A或A2W,W2A代表將PWSTR轉換為PSTR,反之A2W表示將PSTR轉換為PWSTR,見下面的code:
USES_CONVERSION;
PWSTR wszText = L"1.Unicode字符轉換為ANSI;";
PSTR szText="2.ANSI字符轉換成Unicode.";
printf("%s\n",W2A(wszText));
wprintf(L"%s\n",A2W(szText));
注意一點是不要在大循環或者非常長的函數中使用W2A或A2W,具體分析看http://www.cnblogs.com/rainbowzc/archive/2009/09/07/1562168.html。
另一種轉換方法是MultiByteToWideChar和WideCharToMultiByte。MultiByteToWideChar表示將ANSI字符串轉換為Unicode字符串,而WideCharToMultiByte則是將Unicode字符串轉換為ANSI字符串。
MultiByteToWideChar的形式是
int MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTRlpMultiByteStr,
intcchMultiByte,
LPWSTR lpWideCharStr,
intcchWideChar
);
6個參數的含義為:CodePage多字符所對應的的字符集,一般是CP_ACP), dwFlags(一些標准位,一般不需要使用,置0即可),lpMultiByteStr多字符的地址),cchMultiBytes多字符的字符串長度,相當於字符串的字節數,如果是1,則由函數判斷長度), lpWideCharStr寬字符的地址),cchWideChar寬字符的字符長度,相當於字符串的個數)。上面的兩個長度都是包含‘\n’的長度。
如果lpWideCharStr不為NULL,返回轉換成功的長度;如果lpWideCharStr為NULL,返回lpMultiByteStr的字符個數不是字符串長度!!!)。
常用的用法為:
//先將lpWideCharStr置成NULL,從而得到szText的長度。
int iBuffSize =::MultiByteToWideChar(CP_ACP, 0, szText, -1, NULL, 0);
//判斷字符長度是否大於0
if (iBuffSize> 0)
{
LPWSTRwszString = new wchar_t[iBuffSize+1];
int nChars= ::MultiByteToWideChar(CP_ACP, 0, szText, -1, wszString, iBuffSize);
//這是擔心轉換失敗,從而nChars為0,再講wszString置成空字符串
nChars= nChars < iBuffSize ? nChars:iBuffSize;
wszString[nChars]= 0;
}
另一個是寬字符到多字符:
intWideCharToMultiByte(
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cchMultiByte,
LPCSTR lpDefaultChar,
LPBOOL pfUsedDefaultChar
);
這裡比MultiByteToWideChar多兩個參數,而前面的六個參數都是一樣的,lpDefaultChar的作用是當函數遇到一個不能轉換的寬字符時,會用lpDefaultChar來代替,如果lpDefaultChar為NULL時,則用?代替。pfUsedDefaultChar的作用是如果有一個字符沒有成功轉換,則pfUsedDefaultChar為TRUE,否則為FALSE。
int iBuffSize =::WideCharToMultiByte(CodePage, 0, szString, -1, NULL, 0,NULL, false);
if (iBuffSize > 0 )
{
m_pString= new char[iBuffSize];
::WideCharToMultiByte(CodePage, 0,szString, -1, m_pString, iBuffSize, NULL, false);
}
4)PSTR和BSTR的相互轉換
首先是PSTR轉換為BSTR:
BSTR bstrText = _bstr_t(“thisis a bstr”);
BSTR轉換為PSTR:
_bstr_b b = bstrText;
PSTR lpszText = b;