API 函数 MultiByteToWideChar 实现各类编码转换

MultiByteToWideChar和
WideCharToMultiByte用法详解

函数原型:
int WideCharToMultiByte(
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cbMultiByte,
LPCSTR lpDefaultChar,
LPBOOL lpUsedDefaultChar
);
此函数把宽字符串转换成指定的新的字符串,如ANSIUTF8等,新字符串不必是多字节字符集。参数:
CodePage 指定要转换成的字符集代码页,它可以是任何已经安装的或系统自带的字符集,你也可以使用如下所示代码页之一。
参数说明:
1、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
          用 GetLocaleInfo 函数获取当前系统的代码页,936: 简体中文, 950: 繁体中文,949:韩文
2、dwFlags—— 一般用 0 就可以了
          指定是否转换成预制字符或合成的宽字符,对控制字符是否使用像形文字,以及怎样处理无效字符:
          MB_PRECOMPOSED //总是使用预制字符,即有单个预制字符时,就不会使用分解的基字符和不占空间字符。此为函数的默认选项,不能和MB_COMPOSITE合用MB_COMPOSITE //总是使用分解字符,即总是使用基字符+不占空间字符的方式 
MB_ERR_INVALID_CHARS //设置此选项,函数遇到非法字符就失败并返回错误码

ERROR_NO_UNICODE_TRANSLATION,否则丢弃非法字符
MB_USEGLYPHCHARS //使用像形字符代替控制字符
3、lpMultiByteStr //要转换的字符串
4、cbMultiByte //要转换字符串的长度,-1表示转换到字符串结尾。返回原字符串长度。0 作为结束符的字符串
5、lpWideCharStr//接收转换后输出的宽字符串的缓冲,如果为 NULL, 就是代表计算生成的字符串的长度。
6、cchWideChar//输出缓冲区大小,转化生成的 unicode 字符串缓存的容量。如果为0,lpMultiByteStr将被忽略,函数将返回所需缓冲区大小而不同
     为0表示调用失败;当cchWideChar为0时,函数将返回所需缓冲区大小
int BufSize = MultiByteToWideChar(936,0,s,-1,NULL,0); //计算简体中文字符串 s 转成 widestring 之后占用的内存字节数…… //在此处为 wsbuf 分配内存 BufSize 个字节
MultiByteToWideChar(936,0,s,-1,wsbuf,BufSize); //把简体中文字符串 s 转化为 unicode 的 WideString
最常用的应该是CP_ACP和CP_UTF8了,前者将宽字符转换为ANSI,后者转换为UTF8。 

例一:Unicode转换到GBK
#include <Windows.h> 
#define CODE_PAGE_GB18030 54936
int Unicode2GBK( wchar_t *pUnicode, char** ppDest)
{ // get the size of the dest string 
const int size = ::WideCharToMultiByte( CODE_PAGE_GB18030, 0/* you can do more for it*/,

pUnicode, -1, 0, 0, 0, 0 ); 
if ( size == 0 ) { return -1; } 
char* pDestString = new char[size + 2];
::memset( pDestString, 0, sizeof(pDestString) );
// transform
int ret = ::WideCharToMultiByte( CODE_PAGE_GB18030, 0, pUnicode, -1, pDestString, size, 0, 0 );
if( ret == 0 ) { delete pDestString; return -1; }
else { *ppDest = pDestString; return 0; } }
例二:字串转换
wchar_t* pwszUnicode = "Holle, word! 你好,中国! "; 
int iSize; 
char* pszMultiByte; 
iSize = WideCharToMultiByte(CP_ACP, 0, pwszUnicode, -1, NULL, 0, NULL, NULL); 
pszMultiByte = (char*)malloc((iSize+1)/**sizeof(char)*/); 
WideCharToMultiByte(CP_ACP, 0, pwszUnicode, -1, pszMultiByte, iSize, NULL, NULL); 

注意事项:
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 5022150222 50225 50227 50229 52936 54936 57002到5701165000(UTF7) 42(Symbol)
 
     对于UTF8,dwFlags必须为0或WC_ERR_INVALID_CHARS,否则函数都将失败返回并设置错误码ERROR_INVALID_FLAGS,你可以调用GetLastError获得。 
lpUsedDefaultChar:开关变量的指针,用以表明是否使用过默认字符。对于要求此参数为NULL的dwFlags

     而使用此参数,函数将失败返回并设置错误码ERROR_INVALID_PARAMETER。lpDefaultChar和lpUsedDefaultChar都设为NULL,函数会更快一些。

第二个是多字节字符到宽字符转换函数,函数原型如下: 
> int MultiByteToWideChar( 
UINT CodePage, 
DWORD dwFlags, 
LPCSTR lpMultiByteStr, 
int cbMultiByte, 
LPWSTR lpWideCharStr, 
int cchWideChar 
); 
此函数把多字节字符串转换成宽字符串(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 5022150222 50225 50227 50229 52936 54936 57002到5701165000(UTF7) 42(Symbol)
 
对于UTF8,dwFlags必须为0或MB_ERR_INVALID_CHARS,否则函数都将失败并返回错误码ERROR_INVALID_FLAGS。
     以下函数我没用过,只简要说明之。
int GetTextCharset( HDC hdc ); 
此函数获取当前选进的设备描述表的字符集,等同于GetTextCharsetInfo(hdc, NULL, 0)。
返回值: 成功返回字符集标识,失败返回DEFAULT_CHARSET。

1.使用方法详解
在本文开始之处,先简要地说一下何为短字符和宽字符.
所谓的短字符,就是用8bit来表示的字符,典型的应用是ASCII码.而宽字符,顾名思义,就是用16bit表示的字符,典型的有UNICODE.关于windows下的ASCII和UNICODE的更多信息,可以参考这两本经典著作:《windows 程序设计》,《windows 核心编程》.这两本书关于这两种字符都有比较详细的解说.

宽字符转换为多个短字符是一个难点,不过我们只要掌握到其中的要领,便可如鱼得水.
好吧,那就让我们开始吧.
这个是我们需要转化的多字节字符串: 
char sText[20] = {"多字节字符串!OK!"};

我们需要知道转化后的宽字符需要多少个数组空间.虽然在这个里程里面,我们可以直接定义一个20*2宽字符的数组,并且事实上将运行得非常轻松愉快.但假如多字节字符串更多,达到上千个乃至上万个,我们将会发现其中浪费的内存将会越来越多.所以以多字节字符的个数的两倍作为宽字符数组下标的声明绝对不是一个好主意.所幸,我们能够确知所需要的数组空间。我们只需要将MultiByteToWideChar()的第四个形参设为-1,即可返回所需的短字符数组空间的个数:

DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, sText, -1, NULL, 0);
    接下来,我们只需要分配响应的数组空间:
wchar_t *pwText;
pwText = new wchar_t[dwNum];
if(!pwText)
{ delete []pwText; }
接着,我们就可以着手进行转换了.在这里以转换成ASCII码做为例子:
MultiByteToWideChar (CP_ACP, 0, psText, -1, sText, dwSize);

最后,使用完毕当然要记得释放占用的内存:
delete []psText;
   
同理,宽字符转为多字节字符的代码如下: 
wchar_t wText[20] = {L"宽字符转换实例!OK!"};
DWORD dwNum = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);
char *psText;
psText = new char[dwNum];
if(!psText)
{ delete []psText; }
WideCharToMultiByte (CP_OEMCP,NULL,lpcwszStr,-1,psText,dwNum,NULL,FALSE);
delete []psText;
   如果之前我们已经分配好空间,并且由于字符串较短,可以不理会浪费的空间,仅仅只是想简单地将短字符和宽字符相互转换,那有没有什么简便的方法呢?
     WIN32 API里没有符合这种要求的函数,但我们可以自己进行封装:
BOOL MByteToWChar(LPCSTR lpcszStr, LPWSTR lpwszStr, DWORD dwSize)
{
DWORD dwMinSize;
dwMinSize = MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, NULL, 0);
if(dwSize < dwMinSize)
{ return FALSE; }
MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, lpwszStr, dwMinSize); 
return TRUE;
}

BOOL WCharToMByte(LPCWSTR lpcwszStr, LPSTR lpszStr, DWORD dwSize)
{
DWORD dwMinSize;
dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);
if(dwSize < dwMinSize)
{ return FALSE; }
WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,lpszStr,dwSize,NULL,FALSE);
return TRUE;
}
使用方法也很简单,示例如下:
wchar_t wText[10] = {L"函数示例"};
char sText[20]= {0};
WCharToMByte(wText,sText,sizeof(sText)/sizeof(sText[0]));
MByteToWChar(sText,wText,sizeof(wText)/sizeof(wText[0]));
这两个函数的缺点在于无法动态分配内存,在转换很长的字符串时可能会浪费较多内存空间;优点是,在不考虑浪

费空间的情况下转换较短字符串非常方便.

2.MultiByteToWideChar()函数乱码的问题
有的朋友可能已经发现,在标准的WinCE4.2或WinCE5.0 SDK模拟器下,这个函数都无法正常工作,其转换之后

的字符全是乱码.及时更改MultiByteToWideChar()参数也依然如此.
不过这个不是代码问题,其结症在于所定制的操作系统.如果我们定制的操作系统默认语言不是中文,也会出现这种情况.由于标准的SDK默认语言为英文,所以肯定会出现这个问题.而这个问题的解决,不能在简单地更改控制面板的"区域选项"的"默认语言",而是要在系统定制的时候,选择默认语言为"中文".

系统定制时选择默认语言的位置于:Platform -> Setting... -> locale -> default language ,选择"中文",然后编译即可.
Windows CE:不支持参数CodePage中的CP_UTF7和CP_UTF8的值,以及参数dwFlags中的WC_NO_BEST_FIT_CHARS值。
  速查:Windows NT 3.1、Windows 95以上、Windows CE 1.0以上,头文件:winnls.h;库文件:kernel32.lib。

原文地址:https://www.cnblogs.com/qq78292959/p/2077016.html