C/C++宽窄字符转换与输出
IT余识 人气:0前言
如果是C/C++程序员,对于VS应该是不陌生的,可谓是C/C++程序手中的利器
但如果稍微深入学习就会发现,windows API大部分都是分为宽字节与窄字节的,比如常见的MessageboxA与MessageBoxW函数,这时候就会出现很多问题,最常见的便是乱码
需要注意的是,WIndows底层函数均采用的是宽字节,即使你使用的是char,程序真正执行的时候,还是会在底层将char转化为wchar_t,这就意味着使用窄字节效率是比不上宽字节的
同时需要知道.wchar_t是支持多个国家语言的,而char只支持本国语言.
一、什么是宽字节?什么是窄字节?
认识宽窄字节最好的办法就是动手实验一下
可以看到,最直接的影响就是大小,char只占一个字节,而wchar_t要占两个字节,并且需要在字符串前加 L 才表示是宽字节
其实还有很多细节,比如这里是使用的字符c,如果是使用的汉字,还能正常使用吗?很多问题需要自己碰到并解决,最后才能是自己的东西
二、宽窄字节之间的转化方法
1.Windows API进行转化
头文件:
#include<Windows.h>
用到的函数
窄字节转宽字节:
int MultiByteToWideChar( UINT CodePage, //要转换的代码页,一般直接填CP_ACP,表示当前系统使用的代码页 DWORD dwFlags, //转换标志,直接填0即可 LPCCH lpMultiByteStr, //要转换的窄字节字符串 int cbMultiByte, //窄字节字符串的长度,以字节计算 LPWSTR lpWideCharStr, //存放转换完成的宽字符缓冲区 int cchWideChar //存放宽字符缓冲区的大小 );
宽字节转窄字节:
int WideCharToMultiByte( UINT CodePage,//要转换的代码页,一般直接填CP_ACP,表示当前系统使用的代码页 DWORD dwFlags,//转换标志,直接填0即可 LPCWCH lpWideCharStr,//要转换的宽字节字符串 int cchWideChar, //宽字节字符串的长度,以字符计算 LPSTR lpMultiByteStr, //存放转换完成的窄字符缓冲区 int cbMultiByte, //存放宽字符缓冲区的大小 LPCCH lpDefaultChar, //如果字符无法转换,则使用该字符填充,一般填0,默认即可 LPBOOL lpUsedDefaultChar //如果出现无法转换的字符,该参数被设为true,默认填NULL即可 );
一般宽字符是窄字符字节长度的两倍,但也有可能出现意外情况,或者不想自己计算需要多大的缓冲,则可以调用两次该函数,第一次返回需要的大小,第二次进行转换
下面是我封装的两个函数,可直接使用,但需要自己delete内存,可以自己使用wstring和string进行替换
窄字节转宽字节:
//str:要转换的窄字符串 //len:接受转换成功后宽字符的长度,可直接填NULL,不接收 wchar_t* AtoW(const char* str, int* len) { int wcLen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); wchar_t* newBuf = new wchar_t[wcLen + 1]{}; MultiByteToWideChar(CP_ACP, 0, str, -1, newBuf, wcLen); if (len != NULL) { *len = wcLen; } return newBuf; }
宽字节转窄字节
//str:要转换的宽字符串 //len:接受转换成功后窄字符的长度,可直接填NULL,不接收 char* WtoA(const wchar_t* str, int* len) { int cLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, 0, NULL); char* newBuf = new char[cLen + 1]{}; WideCharToMultiByte(CP_ACP, 0, str, -1, newBuf, cLen, 0, NULL); if (len != NULL) { *len = cLen; } return newBuf; }
2.C/C++库函数转换
用到的头文件:
#include<cstdlib> //包含转换函数 #include<locale> //包含设置地域函数
用到的函数:
设置地域,当试图转换中文时,需要设置,否则为乱码
char* setlocale( int _Category, //设置该函数影响范围,一般直接填LC_ALL,即全部影响 char const* _Locale //一般填空,即使用本地地域信息 );
标准窄转宽:
size_t mbstowcs( wchar_t _Dest, //转换后存放的地方 const char * _Source, //要转换的内容 size_t _MaxCount //转换后存放地方的大小,以字符个数计算 )
使用:
#define _CRT_SECURE_NO_WARNINGS //必须定义宏,否则VS报错 #include<iostream> #include<cstdlib> #include<locale> using namespace std; int main() { setlocale(LC_ALL, ""); //设置本地地域信息.否则转换中文出现乱码 wchar_t buf[0xFF]; mbstowcs(buf, "哈哈哈哈", 0xFF); }
标准宽转窄:
size_t wcstombs( char*_Dest, //转换后存放的地方 const wchar_t* _Source, //要转换的内容 size_t _MaxCount //转换后存放地方的大小,以字符个数计算 )
使用:
#define _CRT_SECURE_NO_WARNINGS //必须定义宏,否则VS报错 #include<iostream> #include<cstdlib> #include<locale> using namespace std; int main(){ setlocale(LC_ALL,""); //设置本地地域信息.否则转换中文出现乱码 char buf[0xFF]; wcstombs(buf,L"哈哈哈哈哈",sizeof(buf)); }
安全函数窄转宽:
errno_t mbstowcs_s( size_t* _PtNumOfCharConverted, //接收转换成功的字符个数 wchar_t* _DstBuf, //接受成功转换的字符 size_t _SizeInWords, //_DesBuf缓冲区大小,以字符计算 char const* _SrcBuf, //要转化的字符 size_t _MaxCount //最大要转化的字符数量 );
使用:
#include<iostream> #include<cstdlib> using namespace std; int main() { wchar_t buf[0xFF]; mbstowcs_s(NULL,buf,0xFF, "哈哈哈哈", 0xFF); }
安全函数宽转窄:
errno_t wcstombs_s( size_t* _PtNumOfCharConverted, //接收转换成功的字符个数 wchar_t* _DstBuf, //接受成功转换的字符 size_t _SizeInWords, //_DesBuf缓冲区大小,以字符计算 char const* _SrcBuf, //要转化的字符 size_t _MaxCount //最大要转化的字符数量 );
使用:
#include<iostream> #include<cstdlib> #include<locale> using namespace std; int main() { setlocale(LC_ALL,""); char buf[0xFF]; wcstombs_s(NULL, buf, 0xFF, L"哈哈哈哈哈", sizeof(buf)); printf("%s",buf); }
大家可能看到,我有时使用了setlocal,有时没有使用,这个可以根据具体情况而定,如果出现中文无法转化的情况,就要考虑使用这个函数了
而且我都没有接收转化字符个数,也就是第一个参数,如果需要准确接受转化成功字符的个数,就必须要使用setlocal函数
可能大家还看到过_wcstombs_s_l等函数,这个函数还需要_create_locale与 _free_locale函数配合使用,考虑下来,过于麻烦,不如上面的几种转化方法,所以便不予讲解,大家有兴趣可以去查看官网说明,链接在此 函数说明 设置本地说明
3.ATL库转换
ATL模板库是微软推出的一个C++模板库,在visual studio中安装了C++开发环境就可以正常使用
宽转窄:
#include<iostream> #include<atlconv.h> //头文件 using namespace std; int main() { USES_CONVERSION; //必须添加 wchar_t buf[] = L"哈哈哈哈哈哈"; char *nbuf=W2A(buf); //使用栈空间进行转换,不需要delete cout << nbuf << endl; }
窄转宽:
#include<iostream> #include<locale> #include<atlconv.h> //头文件 using namespace std; int main() { setlocale(LC_ALL,""); //本地化,为输出汉字 USES_CONVERSION; //必须添加 char buf[] = "哈哈哈哈哈哈"; wchar_t *nbuf=A2W(buf); //使用栈空间进行转换,不需要delete wcout << nbuf; }
4.COM组件转换
宽转窄:
#include<comutil.h> #pragma comment(lib, "comsuppw.lib") int main() { wchar_t str[] = L"哈哈哈"; char *s=_com_util::ConvertBSTRToString(str); //转换函数 //其它操作 delete[] s; //释放内存 }
窄转宽:
#include<comutil.h> #pragma comment(lib, "comsuppw.lib") int main() { char str[] = "哈哈哈哈"; wchar_t *s=_com_util::ConvertStringToBSTR(str); //转换函数 //其它操作 SysFreeString(s); //释放内存 }
三.解决VS控制台无法输出宽字符问题
方法一:使用setlocal和printf函数:
#include<iostream> #include<locale> using namespace std; int main() { setlocale(LC_ALL,""); printf("%ls",L"了解"); }
方法二:使用setlocal与wcout
#include<iostream> #include<locale> using namespace std; int main() { setlocale(LC_ALL,""); wcout << L"哈哈哈哈啊"; }
以上两种方法,如果宽字符串中没有中文字符,则可不使用setlocal函数
方法三:使用WriteConsoleW函数
#include<Windows.h> int main() { wchar_t buf[]=L"哈哈哈哈哈哈哈哈哈哈"; WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),buf,sizeof(buf)/2, NULL, 0); }
加载全部内容