1. 程式人生 > >MultiByteToWideChar和WideCharToMultiByte引數詳解及使用方法

MultiByteToWideChar和WideCharToMultiByte引數詳解及使用方法

轉自:https://www.cnblogs.com/ziwuge/archive/2011/11/05/2236968.html https://www.cnblogs.com/gakusei/articles/1585211.html
簡單整理,未驗證,不對結果負責

函式原型:
int MultiByteToWideChar( 
    UINT CodePage, 
    DWORD dwFlags, 
    LPCSTR lpMultiByteStr, 
    int cchMultiByte, 
    LPWSTR lpWideCharStr, 
    int cchWideChar 
  ); 
int WideCharToMultiByte(
    UINT CodePage, 
    DWORD dwFlags, 
    LPWSTR lpWideCharStr, 
    int cchWideChar, 
    LPCSTR lpMultiByteStr, 
    int cchMultiByte, 
    LPCSTR lpDefaultChar, 
    PBOOL pfUsedDefaultChar 
  );


在考慮安全使用的情況下,使用的一般步驟如下:
  MultiByteToWideChar:
  1)呼叫MultiByteToWideChar,為lpWideCharStr引數傳入NULL,為cchWideChar引數傳入0,為cchMultiByte引數傳入-1;
  2)分配一塊足夠容納轉換後Unicode字串的記憶體,它的大小是上一個MultiByteToWideChar呼叫的返回值乘以sizeof(wchar_t);
  3)再次呼叫MultiByteToWideChar,這一次將緩衝區地址作為lpWideCharStr引數的值傳入,將第一次MultiByteToWideChar呼叫的返回值乘以sizeof(wchar_t) 後得到的大小的作為cchWideChar引數的值傳入;
  4)使用轉換後的字串;
  5)釋放Unicode字串佔用的記憶體塊。 

  WideCharToMultiByte:
  採取的步驟和前面的相似,唯一不同的是,返回值直接就是確保轉換成功所需的位元組數,所以無需執行乘法運算。

  在《Windows核心程式設計》中第二章(字元和字串處理)提到很多字元和字串的規範處理方法,如字串函式的問題,到底是使用C庫的呢,還是使用MS自己實現帶_s字尾的。

 
 

  MultiByteToWideChar的與WideCharToMultiByte的引數詳解
  下面部分摘自:http://www.cnblogs.com/wanghao111/archive/2009/05/25/1489021.html#2270293

  WideCharToMultiByte 此函式把寬字串轉換成指定的新的字串,如ANSI,UTF8等,新字串不必是多位元組字符集。
引數:
CodePage: 指定要轉換成的字符集內碼表,它可以是任何已經安裝的或系統自帶的字符集,你也可以使用如下所示內碼表之一。 
     CP_ACP 當前系統ANSI內碼表 
     CP_MACCP 當前系統Macintosh內碼表 
     CP_OEMCP 當前系統OEM內碼表,一種原始裝置製造商硬體掃描碼 
     CP_SYMBOL Symbol內碼表,用於Windows 2000及以後版本,我不明白是什麼 
     CP_THREAD_ACP 當前執行緒ANSI內碼表,用於Windows 2000及以後版本,我不明白是什麼 
     CP_UTF7 UTF-7,設定此值時lpDefaultChar和lpUsedDefaultChar都必須為NULL 
     CP_UTF8 UTF-8,設定此值時lpDefaultChar和lpUsedDefaultChar都必須為NULL  
     /* 我想最常用的應該是CP_ACP和CP_UTF8了,前者將寬字元轉換為ANSI,後者轉換為UTF8。 */




dwFlags: 指定如何處理沒有轉換的字元, 但不設此引數函式會執行的更快一些,我都是把它設為0。 可設的值如下表所示: 
     WC_NO_BEST_FIT_CHARS 把不能直接轉換成相應多位元組字元的Unicode字元轉換成lpDefaultChar指定的預設字元。也就是說,如果把Unicode轉換成多位元組字元,然後再轉換回來,你並不一定得到相同的Unicode字元,因為這期間可能使用了預設字元。此選項可以單獨使用,也可以和其他選項一起使用。 
     WC_COMPOSITECHECK 把合成字元轉換成預製的字元。它可以與後三個選項中的任何一個組合使用,如果沒有與他們中的任何一個組合,則與選項WC_SEPCHARS相同。 
     WC_ERR_INVALID_CHARS 此選項會致使函式遇到無效字元時失敗返回,並且GetLastError會返回錯誤碼ERROR_NO_UNICODE_TRANSLATION。否則函式會自動丟棄非法字元。此選項只能用於UTF8。 
     WC_DISCARDNS 轉換時丟棄不佔空間的字元,與WC_COMPOSITECHECK一起使用 
     WC_SEPCHARS 轉換時產生單獨的字元,此是預設轉換選項,與WC_COMPOSITECHECK一起使用 
     WC_DEFAULTCHAR 轉換時使用預設字元代替例外的字元,(最常見的如’?’),與WC_COMPOSITECHECK一起使用。 
     /*  當指定WC_COMPOSITECHECK時,函式會將合成字元轉換成預製字元。合成字元由一個基字元和一個不佔空間的字元(如歐洲國家及漢語拼音的音標)組成,每一個都有不同的字元值。預製字元有一個用於表示基字元和不佔空間字元的合成體的單一的字元值。 當指定WC_COMPOSITECHECK選項時,也可以使用上表列出的最後3個選項來定製預製字元的轉換規則。這些選項決定了函式在遇到寬字串的合成字元沒有對應的預製字元時的行為,他們與WC_COMPOSITECHECK一起使用,如果都沒有指定,函式預設WC_SEPCHARS。 對於下列內碼表,dwFlags必須為0,否則函式返回錯誤碼ERROR_INVALID_FLAGS。 50220 50221 50222 50225 50227 50229 52936 54936 57002到57011 65000(UTF7) 42(Symbol) 
   對於UTF8,dwFlags必須為0或WC_ERR_INVALID_CHARS,否則函式都將失敗返回並設定錯誤碼ERROR_INVALID_FLAGS,你可以呼叫GetLastError獲得。  */




lpWideCharStr:待轉換的寬字串。 
cchWideChar:待轉換寬字串的長度,-1表示轉換到字串結尾。 
lpMultiByteStr:接收轉換後輸出新串的緩衝區。 
cbMultiByte:輸出緩衝區大小,如果為0,lpMultiByteStr將被忽略,函式將返回所需緩衝區大小而不使用lpMultiByteStr。 
lpDefaultChar:指向字元的指標, 在指定編碼裡找不到相應字元時使用此字元作為預設字元代替。 如果為NULL則使用系統預設字元。對於要求此引數為NULL的dwFlags而使用此引數,函式將失敗返回並設定錯誤碼ERROR_INVALID_PARAMETER。 
lpUsedDefaultChar:開關變數的指標,用以表明是否使用過預設字元。對於要求此引數為NULL的dwFlags而使用此引數,函式將失敗返回並設定錯誤碼ERROR_INVALID_PARAMETER。lpDefaultChar和lpUsedDefaultChar都設為NULL,函式會更快一些。 
  /*  注意:函式WideCharToMultiByte使用不當,會給影響程式的安全。呼叫此函式會很容易導致記憶體洩漏,因為lpWideCharStr指向的輸入緩衝區大小是寬字元數,而lpMultiByteStr指向的輸出緩衝區大小是位元組數。為了避免記憶體洩漏,應確保為輸出緩衝區指定合適的大小。我的方法是先使cbMultiByte為0呼叫WideCharToMultiByte一次以獲得所需緩衝區大小,為緩衝區分配空間,然後再次呼叫WideCharToMultiByte填充緩衝區,詳見下面的程式碼。另外,從Unicode UTF16向非Unicode字符集轉換可能會導致資料丟失,因為該字符集可能無法找到表示特定Unicode資料的字元。  */

返回值:如果函式成功,且cbMultiByte非0,返回寫入lpMultiByteStr的位元組數(包括字串結尾的null);cbMultiByte為0,則返回轉換所需位元組數。函式失敗,返回0。  


  MultiByteToWideChar 是多位元組字元到寬字元轉換函式。
  此函式把多位元組字串轉換成寬字串(Unicode),待轉換的字串並不一定是多位元組的。 
  此函式的引數,返回值及注意事項參見上面函式WideCharToMultiByte的說明,這裡只對dwFlags做簡單解釋。 



dwFlags:指定是否轉換成預製字元或合成的寬字元,對控制字元是否使用像形文字,以及怎樣處理無效字元。 
    MB_PRECOMPOSED 總是使用預製字元,即有單個預製字元時,就不會使用分解的基字元和不佔空間字元。此為函式的預設選項,不能和MB_COMPOSITE合用 
    MB_COMPOSITE 總是使用分解字元,即總是使用基字元+不佔空間字元的方式 
    MB_ERR_INVALID_CHARS 設定此選項,函式遇到非法字元就失敗並返回錯誤碼ERROR_NO_UNICODE_TRANSLATION,否則丟棄非法字元 
    MB_USEGLYPHCHARS 使用像形字元代替控制字元 
    /*  對於下列內碼表,dwFlags必須為0,否則函式返回錯誤碼ERROR_INVALID_FLAGS。50220 50221 50222 50225 50227 50229 52936 54936 57002到57011 65000(UTF7) 42(Symbol)。對於UTF8,dwFlags必須為0或MB_ERR_INVALID_CHARS,否則函式都將失敗並返回錯誤碼ERROR_INVALID_FLAGS */


另外補充一個例子,供大家參考,執行環境(vc 6.0, 32位盜版win7旗艦版)
#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     // TODO: Place code here.
    wchar_t wszTest[] = L"ziwuge";
    wchar_t wszTestNew[] = L"ziwuge部落格園";
    int nwszTestLen = lstrlenW(wszTest);            // 6
    int nwszTestNewLen = lstrlenW(wszTestNew);        // 9
    int nwszTestSize = sizeof(wszTest);                // 14
    int nwszTestNewSize = sizeof(wszTestNew);        //    20
    int nChar = WideCharToMultiByte(CP_ACP, 0, wszTestNew, -1, NULL, 0, NULL, NULL);    // 13, 返回結果已包含'\0'所要佔用的記憶體
    nChar = nChar * sizeof(char);                    // 13, 其實這一步可不需要,請見本文前面解釋
    char* szResult = new char[nChar];
    ZeroMemory(szResult, nChar);
    int i = WideCharToMultiByte(CP_ACP, 0, wszTestNew, -1, szResult, nChar, NULL, NULL);    // 13
    int nszResultLen = lstrlenA(szResult);            // 12
    int nszResultSize = sizeof(szResult);            // 4

    char szTest[] = "ziwuge";
    char szTestNew[] = "ziwuge部落格園";
    int nszTestLen = lstrlenA(szTest);                // 6
    int nszTestNewLen = lstrlenA(szTestNew);        // 12
    int nszTestSize = sizeof(szTest);                // 7
    int nszTestNewSize = sizeof(szTestNew);            // 13
    int nWChar = MultiByteToWideChar(CP_ACP, 0, szTestNew, -1, NULL, 0);        // 10, 返回結果已包含'\0'所要佔用的記憶體
    nWChar = nWChar * sizeof(wchar_t);                // 20
    wchar_t* wszResult = new wchar_t[nWChar];
    ZeroMemory(wszResult, nWChar);
    int j = MultiByteToWideChar(CP_ACP, 0, szTestNew, -1, wszResult, nWChar);    // 10
    int nwszResultLen = lstrlenW(wszResult);        // 9
    int nwszResultSize = sizeof(wszResult);            // 4
    return 0;
}





//=====================================
為了支援Unicode編碼,需要多位元組與寬位元組之間的相互轉換。這兩個系統函式在使用時需要指定內碼表,在實際應用過程中遇到亂碼問題,然後重新閱讀《Windows核心程式設計》,總結出正確的用法。
WideCharToMultiByte的內碼表用來標記與新轉換的字串相關的內碼表。
MultiByteToWideChar的內碼表用來標記與一個多位元組字串相關的內碼表。
常用的內碼表由CP_ACP和CP_UTF8兩個。
使用CP_ACP內碼表就實現了ANSI與Unicode之間的轉換。
使用CP_UTF8內碼表就實現了UTF-8與Unicode之間的轉換。
下面是程式碼實現:
1.  ANSI to Unicode
wstring ANSIToUnicode( const string& str )
{
 int  len = 0;
 len = str.length();
 int  unicodeLen = ::MultiByteToWideChar( CP_ACP,
            0,
            str.c_str(),
            -1,
            NULL,
            0 );  
 wchar_t *  pUnicode;  
 pUnicode = new  wchar_t[unicodeLen+1];  
 memset(pUnicode,0,(unicodeLen+1)*sizeof(wchar_t));  
 ::MultiByteToWideChar( CP_ACP,
         0,
         str.c_str(),
         -1,
         (LPWSTR)pUnicode,
         unicodeLen );  
 wstring  rt;  
 rt = ( wchar_t* )pUnicode;
 delete  pUnicode; 
 
 return  rt;  
}
2.  Unicode to ANSI
string UnicodeToANSI( const wstring& str )
{
 char*     pElementText;
 int    iTextLen;
 // wide char to multi char
 iTextLen = WideCharToMultiByte( CP_ACP,
         0,
         str.c_str(),
         -1,
         NULL,
         0,
NULL,
         NULL );
 pElementText = new char[iTextLen + 1];
 memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );
 ::WideCharToMultiByte( CP_ACP,
         0,
         str.c_str(),
         -1,
         pElementText,
         iTextLen,
         NULL,
         NULL );
 string strText;
 strText = pElementText;
 delete[] pElementText;
 return strText;
}
3.  UTF-8 to Unicode
wstring UTF8ToUnicode( const string& str )
{
 int  len = 0;
 len = str.length();
 int  unicodeLen = ::MultiByteToWideChar( CP_UTF8,
            0,
            str.c_str(),
            -1,
            NULL,
            0 );  
 wchar_t *  pUnicode;  
 pUnicode = new  wchar_t[unicodeLen+1];  
 memset(pUnicode,0,(unicodeLen+1)*sizeof(wchar_t));  
 ::MultiByteToWideChar( CP_UTF8,
         0,
         str.c_str(),
         -1,
         (LPWSTR)pUnicode,
         unicodeLen );  
 wstring  rt;  
 rt = ( wchar_t* )pUnicode;
 delete  pUnicode; 
 
 return  rt;  
}
4.  Unicode to UTF-8    
string UnicodeToUTF8( const wstring& str )
{
 char*     pElementText;
 int    iTextLen;
 // wide char to multi char
 iTextLen = WideCharToMultiByte( CP_UTF8,
         0,
         str.c_str(),
         -1,
         NULL,
         0,
         NULL,
         NULL );
 pElementText = new char[iTextLen + 1];
 memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );
 ::WideCharToMultiByte( CP_UTF8,
         0,
         str.c_str(),
         -1,
         pElementText,
         iTextLen,
         NULL,
         NULL );
 string strText;
 strText = pElementText;
 delete[] pElementText;
 return strText;
}