通过编写串口助手工具学习MFC过程——(三)Unicode字符集的宽字符和多字节字符转换

时间:2022-04-16 05:24:18

通过编写串口助手工具学习MFC过程

因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉。这次通过做一个串口助手再次熟悉一下MFC,并做了一下记录,以便方便以后查阅。做的过程中多是遇到问题直接百度和谷歌搜索来的,所以很多都是不求甚解,知其然不知其所以然。另外做此工具只是为了熟悉了解,许多功能还没有完善!(开发工具VS2008)

(三)Unicode字符集的宽字符和多字节字符转换

在上一节《(二)通过“打开串口”按钮了解基本操作》中提到了宽字符和多字节字符转换的方法,这节就简单的说明一下:

在Visual C++.NET2005中,默认的字符集形式是Unicode,但在VC6.0等工程中,默认的字符集形式是多字节字符集(MBCS:Multi-Byte Character Set),这样导致在VC6.0中非常简单实用的各类字符操作和函数在VS2005环境下运行时会报各种各样的错误,这里总结了在Visual C++.NET2005环境中Unicode字符集下CString和char *之间相互转换的几种方法,其实也就是Unicode字符集与MBCS字符集转换。

1、Unicode下CStringWCHAR *转换为char *

方法一:使用API:WideCharToMultiByte进行转换

CString str = _T("D://校内项目//QQ.bmp");

//注意:以下n和len的值大小不同,n是按字符计算的,len是按字节计算的
              int n = str.GetLength();     // n = 14, len = 18

//获取宽字节字符的大小,大小是按字节计算的
int len = WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),NULL,0,NULL,NULL);

//为多字节字符数组申请空间,数组大小为按字节计算的宽字节字节大小
            char * pFileName = new char[len+1];   //以字节为单位

//宽字节编码转换成多字节编码
WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),pFileName,len,NULL,NULL);

pFileName[len+1] = '/0';   //多字节字符以'/0'结束

方法二:使用函数:T2A、W2A

CString str = _T("D://校内项目//QQ.bmp");

//声明标识符
USES_CONVERSION;

//调用函数,T2A和W2A均支持ATL和MFC中的字符转换
char * pFileName = T2A(str);
             //char * pFileName = W2A(str); //也可实现转换

注意:有时候可能还需要添加引用#include   <afxpriv.h>

2、Unicode下char *转换为CStringWCHAR *

方法一:使用API:MultiByteToWideChar进行转换

char * pFileName = "D://校内项目//QQ.bmp";

//计算char *数组大小,以字节为单位,一个汉字占两个字节
             int charLen = strlen(pFileName);

//计算多字节字符的大小,按字符计算。
int len = MultiByteToWideChar(CP_ACP,0,pFileName,charLen,NULL,0);

//为宽字节字符数组申请空间,数组大小为按字节计算的多字节字符大小
             TCHAR *buf = new TCHAR[len + 1];

//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP,0,pFileName,charLen,buf,len);

buf[len] = '/0'; //添加字符串结尾,注意不是len+1
              //将TCHAR数组转换为CString
              CString pWideChar;
pWideChar.Append(buf);

//删除缓冲区
             delete []buf;

方法二:使用函数:A2T、A2W

char * pFileName = "D://校内项目//QQ.bmp";

USES_CONVERSION;
              CString s = A2T(pFileName);

//CString s = A2W(pFileName);

////

WCHAR * bt = A2T(pFileName);

这里的bt指针变量由A2T函数 自动返回会修改他的初始值。

方法三:使用_T宏,将字符串转换为宽字符

//多字节字符集,在vc6和vc7种可以编译通过的语句,但VS2005不能通过,默认为Unicode字符集
            //AfxMessageBox("加载数据失败",0);

//书写代码使用TEXT("")或_T(""),文本在UNICODE和非UNICODE程序里都通用
AfxMessageBox(_T("加载数据失败"),0);

注意:直接转换在基于MBCS的工程可以,但在基于Unicode字符集的工程中直接转换是不可行的,CString会以Unicode的形式来保存数据,强制类型转换只会返回第一个字符。

引自: http://blog.csdn.net/neverup_/article/details/5664733

注意:

A2T()、A2W() 对char型数组 转成 WCHAR型数组时 转换几个后会出错!!!原因不明。

MultiByteToWideChar()函数效果和A2T()、A2W()执行的效果是一样的。Char型数组转WCHAR数组时,采用按各元素直接赋值的方式。

MultiByteToWideChar()和WideCharToMultiByte()函数与A2T()和T2A()函数测试功能效果相同,但前者使用起来更为复杂,相比后者使用简单。

下面的《MultiByteToWideChar和WideCharToMultiByte的正确使用方法及参数详解》引自:http://www.cnblogs.com/ziwuge/archive/2011/11/05/2236968.html

上面说的对这两个函数测试也是使用的这篇文章的方法,为了防止博主将博文删除,直接复制下来,作为以后参考。

MultiByteToWideChar和WideCharToMultiByte的正确使用方法及参数详解

  本文内容摘自《Windows核心编程》(第五版)Page26。

  文中已经详细阐述了这两个函数的用法,我这里仅仅作为备忘录。函数的参数请参考百度百科MultiByteToWideCharWideCharToMultiByte

函数原型:

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后缀的。

【附】《Windows核心编程》第二章pdf下载:http://dl.dbank.com/c0parcjxsv

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;
}

【参考资料 感谢作者】

http://www.cnblogs.com/wanghao111/tag/%E5%AE%BD%E5%AD%97%E7%AC%A6%E5%BA%93%E5%87%BD%E6%95%B0/