MultiByteToWideChar和WideCharToMultiByte用法詳解
注意:
這兩個函式是由Windows提供的轉換函式,不具有通用性
C語言提供的轉換函式為mbstowcs()/wcstombs()
一、函式簡單介紹
涉及到的標頭檔案:
函式所在標頭檔案:windows.h
#include <windows.h>
wchar_t型別所需標頭檔案:wchar.h
#include <wchar.h>
( 1 ) MultiByteToWideChar()
函式功能:該函式對映一個字串到一個寬字元(unicode)的字串。由該函式對映的字串沒必要是多位元組字元組。
函式原型:
int MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cchMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
引數:
1> CodePage:指定執行轉換的多位元組字元所使用的字符集
這個引數可以為系統已安裝或有效的任何字符集所給定的值。你也可以指定其為下面的任意一值:
Value | Description |
---|---|
CP_ACP | ANSI code page |
CP_MACCP | Not supported |
CP_OEMCP | OEM code page |
CP_SYMBOL | Not supported |
CP_THREAD_ACP | Not supported |
CP_UTF7 | UTF-7 code page |
CP_UTF8 | UTF-8 code page |
2> dwFlags:一組位標記,用以指出是否未轉換成預作或寬字元(若組合形式存在),是否使用象形文字替代控制字元,以及如何處理無效字元。你可以指定下面是標記常量的組合,含義如下:
- MB_PRECOMPOSED:通常使用預作字元——就是說,由一個基本字元和一個非空字元組成的字元只有一個單一的字元值。這是預設的轉換選擇。不能與MB_COMPOSITE值一起使用。
- MB_COMPOSITE:通常使用組合字元——就是說,由一個基本字元和一個非空字元組成的字元分別有不同的字元值。不能與MB_PRECOMPOSED值一起使用。
- MB_ERR_INVALID_CHARS:如果函式遇到無效的輸入字元,它將執行失敗,且GetLastErro返回ERROR_NO_UNICODE_TRANSLATION值。
- MB_USEGLYPHCHARS:使用象形文字替代控制字元。 組合字元由一個基礎字元和一個非空字元構成,每一個都有不同的字元值。每個預作字元都有單一的字元值給基礎/非空字元的組成。在字元è中,e就是基礎字元,而重音符標記就是非空字元。 標記MB_PRECOMPOSED和MB_COMPOSITE是互斥的,而標記MB_USEGLYPHCHARS和MB_ERR_INVALID_CHARS則不管其它標記如何都可以設定。 一般不使用這些標誌,故取值為0時。 3> lpMultiByteStr:指向待轉換的字串的緩衝區。 4> cchMultiByte:指定由引數lpMultiByteStr指向的字串中位元組的個數。可以設定為-1,會自動判斷lpMultiByteStr指定的字串的長度 (如果字串不是以空字元中止,設定為-1可能失敗,可能成功),此引數設定為0函式將失敗。 5> lpWideCharStr:指向接收被轉換字串的緩衝區。 6> cchWideChar:指定由引數lpWideCharStr指向的緩衝區的寬位元組數。若此值為0,函式不會執行轉換,而是返回目標快取lpWideChatStr所需的寬字元數。 返回值: 如果函式執行成功,並且cchWideChar不為0,返回值是由lpWideCharStr指向的緩衝區中寫入的寬字元數;
如果函式執行成功,並且cchMultiByte為0,返回值是待轉換字串的緩衝區所需求的寬字元數大小。(此種情況用來獲取轉換所需的wchar_t的個數)
如果函式執行失敗,返回值為零。
若想獲得更多錯誤資訊,請呼叫GetLastError()函式。它可以返回下面所列錯誤程式碼:
ERROR_INSUFFICIENT_BUFFER; ERROR_INVALID_FLAGS;
ERROR_INVALID_PARAMETER; ERROR_NO_UNICODE_TRANSLATION。 ( 2 ) WideCharToMultiByte() 函式功能:該函式對映一個unicode字串到一個多位元組字串。
函式原型:
int WideCharToMultiByte(
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cchMultiByte,
LPCSTR lpDefaultChar,
LPBOOL pfUsedDefaultChar
);
引數:
與MultiByteToWideChar()函式中的引數類似,但是多了兩個引數:
lpDefaultChar和pfUsedDefaultChar:只有當WideCharToMultiByte函式遇到一個寬位元組字元,而該字元在uCodePage引數標識的內碼表中並沒有它的表示法時,WideCharToMultiByte函式才使用這兩個引數。(通常都取值為NULL)
1> 如果寬位元組字元不能被轉換,該函式便使用lpDefaultChar引數指向的字元。如果該引數是NULL(這是大多數情況下的引數值),那麼該函式使用系統的預設字元。該預設字元通常是個問號。這對於檔名來說是危險的,因為問號是個萬用字元。
2> pfUsedDefaultChar引數指向一個布林變數,如果Unicode字串中至少有一個字元不能轉換成等價多位元組字元,那麼函式就將該變數置為TRUE。如果所有字元均被成功地轉換,那麼該函式就將該變數置為FALSE。當函式返回以便檢查寬位元組字串是否被成功地轉換後,可以測試該變數。
返回值:
如果函式執行成功,並且cchMultiByte不為零,返回值是由 lpMultiByteStr指向的緩衝區中寫入的位元組數;
如果函式執行成功,並且cchMultiByte為零,返回值是接收到待轉換字串的緩衝區所必需的位元組數。(此種情況用來獲取轉換所需Char的個數)
如果函式執行失敗,返回值為零。
若想獲得更多錯誤資訊,請呼叫GetLastError函式。它可以返回下面所列錯誤程式碼:
ERROR_INSUFFICIENT_BJFFER;ERROR_INVALID_FLAGS;
ERROR_INVALID_PARAMETER;ERROR_NO_UNICODE_TRANSLATION。
二、使用方法
( 1 ) 將多位元組字串轉為寬字串:
1) 呼叫MultiByteToWideChar()函式,設定cchWideChar引數為0(用以獲取轉換所需的接收緩衝區大小);
2) 獲取輸入快取的大小,作為cchMultiByte的值;(這樣做是為了節省空間,也可以給cchMultiByte取值-1(字串需要以空字元結尾,否則會出錯))
3) 分配足夠的記憶體塊,用於存放轉換後的Unicode字串;
該記憶體塊的大小由前面對cchWideChar()函式的返回值來決定;(也可以用別的方法,但該方法更節省記憶體)
4) 再次呼叫MultiByteToWideChar()函式,這次將快取的地址作為lpWideCharStr,引數來傳遞,並傳遞第一次呼叫MultiByteToWideChar()函式時的返回值作為cchWideChar引數的值;
5) 使用轉換後的字串;
6) 釋放接收緩衝區佔用的記憶體塊;
示例程式碼:
void main()
{
char sBuf[25]={0};
strcpy(sBuf, "我最棒");
//獲取輸入快取大小
int sBufSize=strlen(sBuf);
//獲取輸出快取大小
//VC++ 預設使用ANSI,故取第一個引數為CP_ACP
DWORD dBufSize=MultiByteToWideChar(CP_ACP, 0, sBuf, sBufSize, NULL, 0);
printf("需要wchar_t%u個\n", dBufSize);
wchar_t * dBuf=new wchar_t[dBufSize];
wmemset(dBuf, 0, dBufSize);
//進行轉換
int nRet=MultiByteToWideChar(CP_ACP, 0, sBuf, sBufSize, dBuf, dBufSize);
if(nRet<=0)
{
cout<<"轉換失敗"<<endl;
DWORD dwErr=GetLastError();
switch(dwErr)
{
case ERROR_INSUFFICIENT_BUFFER:
printf("ERROR_INSUFFICIENT_BUFFER\n");
break;
case ERROR_INVALID_FLAGS:
printf("ERROR_INVALID_FLAGS\n");
break;
case ERROR_INVALID_PARAMETER:
printf("ERROR_INVALID_PARAMETER\n");
break;
case ERROR_NO_UNICODE_TRANSLATION:
printf("ERROR_NO_UNICODE_TRANSLATION\n");
break;
}
}
else
{
cout<<"轉換成功"<<endl;
cout<<dBuf;
}
delete(dBuf);
}
注意:兩次呼叫MultiCharToWideChar()時,形參cchMultiByte的取值需要相同,否則可能會出現接收快取不足之類的錯誤,從而導致轉換失敗!
( 2 ) 從寬位元組轉為窄位元組字串
步驟與(1)類似,故不贅述
程式碼示例如下:
//從寬字串轉換窄字串
wchar_t sBuf[25]={0};
wcscpy(sBuf, L"我最棒");
//獲取轉換所需的目標快取大小
DWORD dBufSize=WideCharToMultiByte(CP_OEMCP, 0, sBuf, -1, NULL,0,NULL, FALSE);
//分配目標快取
char *dBuf = new char[dBufSize];
memset(dBuf, 0, dBufSize);
//轉換
int nRet=WideCharToMultiByte(CP_OEMCP, 0, sBuf, -1, dBuf, dBufSize, NULL, FALSE);
if(nRet<=0)
{
printf("轉換失敗\n");
}
else
{
printf("轉換成功\nAfter Convert: %s\n", dBuf);
}
delete []dBuf;
三、MultiByteToWideChar()函式亂碼的問題
有的朋友可能已經發現,在標準的WinCE4.2或WinCE5.0 SDK模擬器下,這個函式都無法正常工作,其轉換之後的字元全是亂碼!
及時更改MultiByteToWideChar()引數也依然如此。不過這個不是程式碼問題,其結症在於所定製的作業系統.如果我們定製的作業系統預設語言不是中文,也會出現這種情況。
由於標準的SDK預設語言為英文,所以肯定會出現這個問題。而這個問題的解決,不能在簡單地更改控制面板的”區域選項”的”預設語言”,而是要在系統定製的時候,選擇預設語言為”中文”。系統定製時選擇預設語言的位置於: Platform -> Setting… -> locale -> default language ,選擇”中文”,然後編譯即可。
Unicode :寬位元組字符集
- 如何取得一個既包含單位元組字元又包含雙位元組字元的字串的字元個數? 可以呼叫Microsoft Visual C++的執行期庫包含函式_mbslen來操作多位元組(既包括單位元組也包括雙位元組)字串。 呼叫strlen函式,無法真正瞭解字串中究竟有多少字元,它只能告訴你到達結尾的0之前有多少個位元組。
- 如何對DBCS(雙位元組字符集)字串進行操作? 函式 描述 PTSTR CharNext ( LPCTSTR ); 返回字串中下一個字元的地址 PTSTR CharPrev ( LPCTSTR, LPCTSTR ); 返回字串中上一個字元的地址 BOOL IsDBCSLeadByte( BYTE ); 如果該位元組是DBCS字元的第一個位元組,則返回非0值
為什麼要使用Unicode?
可以很容易地在不同語言之間進行資料交換。 使你能夠分配支援所有語言的單個二進位制.exe檔案或DLL檔案。 提高應用程式的執行效率。 Windows 2000是使用Unicode從頭進行開發的,如果呼叫任何一個Windows函式並給它傳遞一個ANSI字串,那麼系統首先要將字串轉換成Unicode,然後將Unicode字串傳遞給作業系統。如果希望函式返回ANSI字串,系統就會首先將Unicode字串轉換成ANSI字串,然後將結果返回給你的應用程式。進行這些字串的轉換需要佔用系統的時間和記憶體。通過從頭開始用Unicode來開發應用程式,就能夠使你的應用程式更加有效地執行。 Windows CE 本身就是使用Unicode的一種作業系統,完全不支援ANSI Windows函式 Windows 98 只支援ANSI,只能為ANSI開發應用程式。 Microsoft公司將COM從16位Windows轉換成Win32時,公司決定需要字串的所有COM介面方法都只能接受Unicode字串。
如何編寫Unicode原始碼? Microsoft公司為Unicode設計了WindowsAPI,這樣,可以儘量減少程式碼的影響。實際上,可以編寫單個原始碼檔案,以便使用或者不使用Unicode來對它進行編譯。只需要定義兩個巨集(UNICODE和_UNICODE),就可以修改然後重新編譯該原始檔。 _UNICODE巨集用於C執行期標頭檔案,而UNICODE巨集則用於Windows標頭檔案。當編譯原始碼模組時,通常必須同時定義這兩個巨集。
Windows定義的Unicode資料型別有哪些? 資料型別 說明 WCHAR Unicode字元 PWSTR 指向Unicode字串的指標 PCWSTR 指向一個恆定的Unicode字串的指標 對應的ANSI資料型別為CHAR,LPSTR和LPCSTR。 ANSI/Unicode通用資料型別為TCHAR,PTSTR,LPCTSTR。
如何對Unicode進行操作? 字符集 特性 例項 ANSI 操作函式以str開頭 strcpy Unicode 操作函式以wcs開頭 wcscpy MBCS 操作函式以_mbs開頭 _mbscpy ANSI/Unicode 操作函式以_tcs開頭 _tcscpy(C執行期庫) ANSI/Unicode 操作函式以lstr開頭 lstrcpy(Windows函式) 所有新的和未過時的函式在Windows2000中都同時擁有ANSI和Unicode兩個版本。ANSI版本函式結尾以A表示;Unicode版本函式結尾以W表示。Windows會如下定義:
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE
7.列表內容 如何表示Unicode字串常量? 字符集 例項 ANSI “string” Unicode L“string” ANSI/Unicode T(“string”)或_TEXT(“string”)if( szError[0] == _TEXT(‘J’) ){ } 7. 為什麼應當儘量使用作業系統函式? 這將有助於稍稍提高應用程式的執行效能,因為作業系統字串函式常常被大型應用程式比如作業系統的外殼程序Explorer.exe所使用。由於這些函式使用得很多,因此,在應用程式執行時,它們可能已經被裝入RAM。 如:StrCat,StrChr,StrCmp和StrCpy等。 8. 如何編寫符合ANSI和Unicode的應用程式? (1) 將文字串視為字元陣列,而不是chars陣列或位元組陣列。 (2) 將通用資料型別(如TCHAR和PTSTR)用於文字字元和字串。 (3) 將顯式資料型別(如BYTE和PBYTE)用於位元組、位元組指標和資料快取。 (4) 將TEXT巨集用於原義字元和字串。 (5) 執行全域性性替換(例如用PTSTR替換PSTR)。 (6) 修改字串運算問題。例如函式通常希望在字元中傳遞一個快取的大小,而不是位元組。這意味著不應該傳遞sizeof(szBuffer),而應該傳遞(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要為字串分配一個記憶體塊,並且擁有該字串中的字元數目,那麼請記住要按位元組來分配記憶體。這就是說,應該呼叫malloc(nCharacters *sizeof(TCHAR)),而不是呼叫malloc(nCharacters)。 9. 列表內容 如何對字串進行有選擇的比較? 通過呼叫CompareString來實現。 標誌 含義 NORM_IGNORECASE 忽略字母的大小寫 NORM_IGNOREKANATYPE 不區分平假名與片假名字元 NORM_IGNORENONSPACE 忽略無間隔字元 NORM_IGNORESYMBOLS 忽略符號 NORM_IGNOREWIDTH 不區分單位元組字元與作為雙位元組字元的同一個字元 SORT_STRINGSORT 將標點符號作為普通符號來處理 10. . 如何判斷一個文字檔案是ANSI還是Unicode? 判斷如果文字檔案的開頭兩個位元組是0xFF和0xFE,那麼就是Unicode,否則是ANSI。 11. 如何判斷一段字串是ANSI還是Unicode? 用IsTextUnicode進行判斷。IsTextUnicode使用一系列統計方法和定性方法,以便猜測快取的內容。由於這不是一種確切的科學方法,因此 IsTextUnicode有可能返回不正確的結果。 12. 如何在Unicode與ANSI之間轉換字串? Windows函式MultiByteToWideChar用於將多位元組字串轉換成寬字串;函式WideCharToMultiByte將寬字串轉換成等價的多位元組字串。