代码编译运行环境:VC++2012+Debug+Win32
char字符与wchar_t字符由于编码不同,所以在char*和wchar_t*之间使用强制类型转换达不到正确转换字符串的目的。考察如下程序。
#include <iostream>
using namespace std;
int main(){
wchar_t* str=L"ABC我们";
char* s=(char*)str;
cout<<s<<endl;
}
输出结果出错:只输出A。经过强制类型转换,s指向了宽字符编码字符串,字符串数据没有发生任何变化,只是用多字节字符字符编码重新对它进行解释,自然输出的结果是错误的。
要想将宽字符编码字符串转换成多字节编码字符串(或者反过来),必须先读懂原来的字符串,然后再重新对它进行编码。只有这样才能到达转换的目的。由于宽字符可以表示多国语言的文字,因此,下文的讨论限于中文的宽字符串与多字节字符串之间的相互转换。在不同的才做系统上,有一些特殊的库函数可以用来进行字符编码之间的转换。在VC++中,其中关键的几个函数是setlocale()、wcstombs_s()和mbstowcs_s()。
(1)setlocale()
功能:配置地域化信息;
头文件:< locale.h>;
函数原型: char *setlocale (int category, const char * locale);
函数参数:
category表示对本地化的某项内容进行设置,可取如下值:
LC_ALL 包括下面的全部选项都要;
LC_COLLATE 配置字符串比较;
C_CTYPE 配置字符类别及转换,例如全变大写 strtoupper();
LC_MONETARY 配置金融货币;
LC_NUMERIC 配置小数点后的位数;
LC_TIME 配置时间日期格式,与 strftime() 合用。
locale表示本地域代号:
如果为NULL,则返回当前的locale名称(一般为C);如果非空,则根据category和locale进行设置,如果成功,则返回新的locale名称(地域名称),如果失败,则返回 NULL。
(2)wcstombs_s ()
功能:将宽字符编码字符串转换成多字节编码字符串
头文件: < stdlib.h>
函数原型:
errno_t __cdecl wcstombs_s(size_t * _PtNumOfCharConverted, char * _Dst, size_t _DstSizeInBytes, const wchar_t * _Src, size_t _MaxCountInBytes);
函数参数:
PtNumOfCharConverted:指向转换后的字符串的长度加上结束符(单位字节);
Dst:指向转换后的字符串首地址;
DstSizeInBytes:目的地址最大字节空间(单位字节);
_Src:源宽字符串首地址;
_MaxCountInBytes:最多可存入多字节字符串缓冲最的字节数,用于裁剪转换后的字符串。
返回值:成功返回0, 失败则返回失败代码。
(3)mbstowcs_s ()
函数功能:将多字节编码字符串转换成宽字符编码字符串
头文件:< stdlib.h>
函数原型:
errno_t __cdecl mbstowcs_s(size_t * _PtNumOfCharConverted, wchar_t * _DstBuf, size_t _SizeInWords, const char * _SrcBuf, size_t _MaxCount );
参数说明:
PtNumOfCharConverted:指向转换后的字符串的长度加上结束符(单位wchar_t);
_DstBuf:指向转换后的字符串首地址;
_SizeInWords:目的地址最大字空间大小(单位wchar_t);
_SrcBuf:源多字节字符串首地址;
_MaxCount:最多可存入宽字符串缓冲中的字符个数,用于裁剪转换后的宽字符串。
返回值:成功返回0, 失败则返回失败代码。
#include <iostream>
#include <locale.h>
#include <string>
using namespace std;
string ws2s(const wstring& ws){
size_t convertedChars=0;
string curLocale=setlocale(LC_ALL,NULL); //curLocale="C"
setlocale(LC_ALL,"chs");
const wchar_t* wcs=ws.c_str();
size_t dByteNum=sizeof(wchar_t)*ws.size()+1;
cout<<"ws.size():"<<ws.size()<<endl; //5
char* dest=new char[dByteNum];
wcstombs_s(&convertedChars,dest,dByteNum,wcs,_TRUNCATE);
cout<<"convertedChars:"<<convertedChars<<endl; //8
string result=dest;
delete[] dest;
setlocale(LC_ALL,curLocale.c_str());
return result;
}
wstring s2ws(const string& s){
size_t convertedChars=0;
string curLocale=setlocale(LC_ALL,NULL); //curLocale="C"
setlocale(LC_ALL,"chs");
const char* source=s.c_str();
size_t charNum=sizeof(char)*s.size()+1;
cout<<"s.size():"<<s.size()<<endl; //7
wchar_t* dest=new wchar_t[charNum];
mbstowcs_s(&convertedChars,dest,charNum,source,_TRUNCATE);
cout<<"s2ws_convertedChars:"<<convertedChars<<endl; //6
wstring result=dest;
delete[] dest;
setlocale(LC_ALL,curLocale.c_str());
return result;
}
int main(){
wchar_t* wstr=L"ABC我们";
char* str="ABC我们";
string obj=ws2s(wstr);
cout<<obj<<endl;
setlocale(LC_ALL, "chs");
//或者wcout.imbue(locale("chs"));
wstring objw=s2ws(str);
wcout<<objw<<endl;
}
程序输出:
ABC我们
ABC我们
程序运行结果表明,char*到wchar_t*的双向转换时成功的,但要注意的是,执行转换的函数mbstowcs_s和wcstombs_s的运行是依赖于当前的locale设置。在程序中去除相关的setlocale()函数调用,就得不到正确的结果。
除了可以利用C/C++库函数解决字符编码的转换问题,还可以利用特定操作系统下提供的函数。例如,可以利用Windows API实现字符编码的转换。考察如下程序。
#include <windows.h>
#include <iostream>
using namespace std;
int main(){
wchar_t* ws=L"测试字符串";
char* ss="ABC我们";
int bufSize=WideCharToMultiByte(CP_ACP,NULL,ws,-1,NULL,0,NULL,FALSE);
cout<<bufSize<<endl;
char *sp=new char[bufSize];
WideCharToMultiByte(CP_ACP,NULL,ws,-1,sp,bufSize,NULL,FALSE);
cout<<sp<<endl;
delete[] sp;
bufSize=MultiByteToWideChar(CP_ACP,0,ss,-1,NULL,0);
cout<<bufSize<<endl;
wchar_t* wp=new wchar_t[bufSize];
MultiByteToWideChar(CP_ACP,0,ss,-1,wp,bufSize);
wcout.imbue(locale("chs"));
}
程序输出结果:
11
测试字符串
6
ABC我们
其中函数int bufSize=WideCharToMultiByte(CP_ACP,NULL,ws,-1,NULL,0,NULL,FALSE);
是用来获取宽字符串转换成多字节字符串所所占据的空间的大小(单位字节),这是将第5个参数设置为NULL达到的效果。同样,函数调用bufSize=MultiByteToWideChar(CP_ACP,0,ss,-1,NULL,0);
是用来获取多字节字符串转换成宽字节字符串后所占用空间的大小(单位宽字符个数),这是将第5个参数设置为NULL之后达到的效果。
以下将具体讲解上面两个关键函数。
(1)WideCharToMultiByte()
函数功能:将宽字符串转换成多字节字符串
头文件:< windows.h>
函数原型:
WINAPI
WideCharToMultiByte(
_In_ UINT CodePage,
_In_ DWORD dwFlags,
_In_NLS_string_(cchWideChar) LPCWCH lpWideCharStr,
_In_ int cchWideChar,
_Out_writes_bytes_to_opt_(cbMultiByte, return) LPSTR lpMultiByteStr,
_In_ int cbMultiByte,
_In_opt_ LPCCH lpDefaultChar,
_Out_opt_ LPBOOL lpUsedDefaultChar
);
参数详解:
CodePage:指定执行转换的代码页字符集,可以为操作系统已安装或有效的任何代码页字符集,也可以指定其为下面的任意一值:
CP_ACP:ANSI代码页;CP_ACP:ANSI代码页;CP_MACCP:Macintosh代码页;CP_OEMCP:OEM代码页;CP_SYMBOL:符号代码页;CP_THREAD_ACP:当前线程ANSI代码页;CP_UTF7:使用UTF-7转换;CP_UTF8:使用UTF-8转换。使用最多的就是CP_ACP和CP_UTF8;
dwFlags:指定如何处理没有转换成功的字符,也可以不设此参数(设置为0),函数会运行的更快一些。对于UTF-8,dwflags必须为0或者WC_ERR_INVALID_CHARS,否则函数都将失败返回并设置错误码ERROR_INVALID_FLAGS,可以调用GetLastError获得;
lpWideCharStr:待转换为宽字符串;
cchWideChar:待转换的宽字符串的长度(字符个数),-1表示转换到字符串结尾;
lpMultiByteStr:转换后目的字符串缓冲区;
cbMultiByte:目的字符串缓冲区大小(单位字节)。如果设置为0,函数将返回所需缓冲区大小而忽略lpMultiByteStr;
lpDefaultChar:指向字符的指针,在指定编码里找不到相应字符时使用此字符作为默认字符替代。如果为NULL,则使用系统默认字符。使用dwFlags时不能使用此参数,否则报ERROR_INVLID_PARAMETER错误。
lpUsedDefaultChar:开关变量的指针,表明是否使用过默认字符。对于要求此参数为NULL的dwflags而使用此参数,函数将失败返回,并设置错误码ERROR_INVLID_PARAMETER。lpDefaultChar和lpUsedDefaultChar都设为NULL,函数会更快一些。
函数返回值:如果函数运行成功,并且cbMultiByte不为零,返回值是由 lpMultiByteStr指向的缓冲区中写入的字节数;如果函数运行成功,并且cbMultiByte为零,返回值是接存放目的字符串缓冲区所必需的字节数。如果函数运行失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。
(2)MultiByteToWideChar()
函数功能:多字节字符串到款字节字符串的转换;
头文件:< windows.h>
函数原型:
int WINAPI MultiByteToWideChar(
_In_ UINT CodePage,
_In_ DWORD dwFlags,
_In_NLS_string_(cbMultiByte) LPCCH lpMultiByteStr,
_In_ int cbMultiByte,
_Out_writes_to_opt_(cchWideChar, return) LPWSTR lpWideCharStr,
_In_ int cchWideChar
);
参数详解:
CodePage:同上;
dwFlags:指定是否转换成预制字符或合成的宽字符,是否使用象形文字替代控制字符,以及如何处理无效字符。对于UTF-8,dwflags必须为0或者WC_ERR_INVALID_CHARS,否则函数都将失败返回并设置错误码ERROR_INVALID_FLAGS,可以调用GetLastError获得;
lpMultiByteStr:多字节字符串;
cbMultiByte:待转换的多字节字符串长度,-1表示转换到字符串结尾;
lpWideCharStr:存放转换后的宽字符串缓冲;
cchWideChar:宽字符串缓冲的大小(单位字符数)。
返回值:如果函数运行成功,并且cchWideChar不为零,返回值是由 lpWideCharStr指向的缓冲区中写入的字符数;如果函数运行成功,并且cchWideChar为零,返回值是接存放目的字符串缓冲区所必需的字符数。如果函数运行失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。
[1]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008[P340-P344]
[2]百度百科.MultiByteToWideChar