【49】MFC入门到精通——MFC string转CString、 CString转string 【Unicode、多字节编码下】、【CT2A /CW2A、CA2T/CA2W】

1 string --> CString

在使用MFC时,遇到了CString与string转换的问题,特此记录下来。其实CString与string的转换方式有挺多种的,但也并不是每一种都适用,可能需要一些稍微的改动才能正常运行。

	std::string str3 = "你好呀! hello 123 ";

	CString CStr = str3.c_str();

比如网上常见的一种转换方法(如果你的能直接转换也是没问题滴):

在这里插入图片描述

发现转不了,就很气。提示错误翻译一下大概就是说:没有合适的构造函数来进行那个类型的转换。

提示错误为:

no suitable constructor exists to convert from “const char *” to “ATL::CStringT<wchar_t, StrTraitMFC_DLL<wchar_t, 
ATL::ChTraitsCRT<wchar_t>>>,
严重性	代码	说明	项目	文件	行	禁止显示状态
错误	C2440	“初始化”: 无法从“const _Elem *”转换为“ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>”	
MFCApplication1	D:\Projects\MFC\MFCApplication1\MFCApplication1\MFCApplication1Dlg.cpp	610	
严重性	代码	说明	项目	文件	行	禁止显示状态
错误(活动)	E0415	不存在从 "const char *" 转换到 "ATL::CStringT<wchar_t, StrTraitMFC_DLL<wchar_t, ATL::ChTraitsCRT<wchar_t>>>" 
的适当构造函数	MFCApplication1	D:\Projects\MFC\MFCApplication1\MFCApplication1\MFCApplication1Dlg.cpp	610	

为什么呢?原因就是CString c_name = name.c_str();其实是需要调用拷贝构造函数的,但是类型不匹配,所以找不到合适的构造函数。

1.1 Unicode编码下

1.1.1 方法1:ATL字符串转换宏

该方法是比较通用的,通过使用ATL字符串转换宏。

	std::string str1 = "hello !";
	std::string str2 = "你好!";
	std::string str3 = "你好呀! hello 123 ";

	CString CStr,cstr1, cstr2, cstr3;


	cstr1 = CA2T(str1.c_str()); //所以CA2T也就是CA2W就是将多字符集转换为宽字符UNICODE,也可写成CA2W。
	cstr2 = CA2T(str2.c_str()); 
	cstr3 = CA2W(str3.c_str());//写成CA2W,试试看

	CStr = cstr1 + "\r\n" + cstr2 + "\r\n" + cstr3;

	AfxMessageBox(CStr);

在这里插入图片描述

cstr1 = CA2T(str1.c_str()); //所以CA2T也就是CA2W就是将多字符集转换为宽字符UNICODE,也可写成CA2W

1.1.2 方法2:调用“赋值运算符重载” (区别于直接赋值)

修改编码可能导致程序中其它地方出现错误(我的就是- -!)。所以可以调用“赋值运算符重载”的方式来完成转换,很简单也很实用。不用修改编码方式。

	std::string str3 = "你好呀! hello 123 ";

	CString CStr;

	CStr = str3.c_str();//注意这里使用的是 =运算符的重载,注意与直接赋值的区别

	AfxMessageBox(CStr);

在这里插入图片描述

注意二者区别,定义时,直接赋值,是不行的。

在这里插入图片描述

混合字符串

	std::string str1 = "hello !";
	std::string str2 = "你好!";
	std::string str3 = "你好呀! hello 123 ";

	CString CStr,cstr1, cstr2, cstr3;

	cstr1 = str1.c_str(); 
	cstr2 = str2.c_str(); 
	cstr3 = str3.c_str();

	CStr = cstr1 + "\r\n" + cstr2 + "\r\n" + cstr3;

	AfxMessageBox(CStr);

在这里插入图片描述

这种方式在 Unicode编码、多字节编码 下通用

1.1.3 自己封装函数 Str2Cstr(string str)

也可以直接写个函数用:

CString Str2Cstr(string str)
{
	return CString(str.c_str());
}

调用

void CMFCtestDlg::OnBnClickedButton2()
{
	// TODO: 在此添加控件通知处理程序代码

	std::string str3 = "你好呀! hello 123 ";
	CString CStr;
	CStr = Str2Cstr(str3.c_str());

	AfxMessageBox(CStr);
}

在这里插入图片描述

也可以这样写

	std::string str3 = "你好呀! hello 123 ";
	CString CStr = Str2Cstr(str3.c_str());
	AfxMessageBox(CStr);

还可以 强制转换

std::string str3 = "你好呀! hello 123 ";
AfxMessageBox(CString(str3.c_str()));

在这里插入图片描述

1.2 “多字节”编码方式

1.2.1 方法1:改为 “多字节”编码方式

通过修改编码方式,可以解决。我的是在Unicode编码方式下不能使用,修改为“多字节”编码方式即可。

右键项目->“属性”->选择“多字节”。

在这里插入图片描述

注意要在 x64模式下,x86不行
在这里插入图片描述

1.2.2 方法2: “多字节”编码下 格式化转换

右键项目->“属性”->选择“多字节”。

在这里插入图片描述

	std::string str1 = " hello 123 ";
	std::string str3 = "你好呀! hello 123 ";

	CString CStr;

	CStr.Format("%s", str3.c_str());

	AfxMessageBox(CStr);

注意要在 x64模式下,x86不行
在这里插入图片描述

1.3 string --> CString/CStringW / wstring

//std::string --> CString/CStringW / std::wstring
使用Unicode字符集时,CString等价于CStringW;


	std::string str1 = "你好呀! hello 123 ";

	//使用Unicode字符集时,CString等价于CStringW;
	//CString CStr(CStringA(str1.c_str()));
	CStringW CStr(CStringA(str1.c_str()));

	std::wstring strw(CStr);

	AfxMessageBox(CStr);
	GetDlgItem(IDC_STATIC_STR)->SetWindowTextW(strw.c_str());

在这里插入图片描述

2 CString --> string

转换完之后,最好将 CString释放掉

	CStr1.ReleaseBuffer();
	CStr2.ReleaseBuffer();

2.1 多字节编码下

右键项目->“属性”->选择“多字节”。

2.1.1 CStr.GetBuffer()

在这里插入图片描述

    CString CStr1 = _T("hello 123");
	CString CStr2 = _T("你好呀! hello 123");

	//// GetString()比较新的VS有,旧可以用GetBuffer(),我这里VS2019,两个都可以用
	std::string str1 = CStr1.GetBuffer();
	std::string str2 = CStr2.GetString();

	//MFC中输出 cout 
	AllocConsole();                   //AllocConsole函数的功能是为当前的窗口程序申请一个Console窗口
	freopen("CONOUT$", "w", stdout);  // freopen函数用来替换一个流,或者说重新分配文件指针,以实现重定向。
	std::cout << str1 << "\r\n" << str2 << std::endl;

在这里插入图片描述
赋值运算符 ,或者 =运算符的重载,都是可以的

	CString CStr1 = _T("hello 123");
	CString CStr2 = _T("你好呀! hello 123");

	std::string str1;
	std::string str2 ;

	str1 = CStr1.GetBuffer();
	str2 = CStr2.GetBuffer();

2.1.2 string(CStringA(CStr))

CString CStr1 = _T("你好呀! hello 123");

	CStringA cstra(CStr1);
	std::string str1(cstra);
	//或
	std::string(CStringA(CStr1));


	//MFC中输出 cout 
	AllocConsole();                   //AllocConsole函数的功能是为当前的窗口程序申请一个Console窗口
	freopen("CONOUT$", "w", stdout);  // freopen函数用来替换一个流,或者说重新分配文件指针,以实现重定向。
	std::cout << str1 << "\r\n" << std::string(CStringA(CStr1))<<std::endl;

在这里插入图片描述

2.2 Unicode编码下

2.2.1 CT2A /CW2A(CStr.GetString())

    //CT2A含义
	//C:convert,转换的意思
	//T:中间类型,如果定义了_UNICODE,则T表示W;如果定义了_MBCS,则T表示A
	//W:宽字符串,也就是UNICODE
	//A:ANSI字符串,也就是Muti-Byte。


	CString CStr1 = _T("hello 123");
	CString CStr2 = _T("你好呀! hello 123");

	//所以CT2A其实就是CW2A就是将Unicode转换为多字符集ASCII,也可写成CW2A
	//// GetString()比较新的VS有,旧可以用GetBuffer(),我这里VS2019,两个都可以用

	std::string str1 = CT2A(CStr1.GetString());
	std::string str2 = CT2A(CStr2.GetBuffer());

	//MFC中输出 cout 
	AllocConsole();                   //AllocConsole函数的功能是为当前的窗口程序申请一个Console窗口
	freopen("CONOUT$", "w", stdout);  // freopen函数用来替换一个流,或者说重新分配文件指针,以实现重定向。
	std::cout << str1 << "\r\n" << str2 << std::endl;

在这里插入图片描述

2.2.2 CT2A /CW2A(CStr)

//CT2A含义
	//C:convert,转换的意思
	//T:中间类型,如果定义了_UNICODE,则T表示W;如果定义了_MBCS,则T表示A
	//W:宽字符串,也就是UNICODE
	//A:ANSI字符串,也就是Muti-Byte。


	CString CStr2 = _T("你好呀! hello 123");

	//所以CT2A其实就是CW2A就是将Unicode转换为多字符集ASCII,也可写成CW2A
	std::string str1 = CW2A(CStr2);
	std::string str2 = CT2A(CStr2);


	//MFC中输出 cout 
	AllocConsole();                   //AllocConsole函数的功能是为当前的窗口程序申请一个Console窗口
	freopen("CONOUT$", "w", stdout);  // freopen函数用来替换一个流,或者说重新分配文件指针,以实现重定向。
	std::cout << str1 <<"\n" << str2 <<std::endl;

在这里插入图片描述

2.2.3 W2A(CStr)


	CString CStr1 = L"你好呀! hello 123";
	CString CStr2 = _T("你好呀! hello 123");

	USES_CONVERSION;
	std::string str1 = W2A(CStr1);
	std::string str2 = W2A(CStr2);
	//首先str--》const wchar_t* ,然后W2A将const wchar_t*--》const char*,
    //最后用const char*初始化s
 

	//MFC中输出 cout 
	AllocConsole();                   //AllocConsole函数的功能是为当前的窗口程序申请一个Console窗口
	freopen("CONOUT$", "w", stdout);  // freopen函数用来替换一个流,或者说重新分配文件指针,以实现重定向。
	std::cout << str1 << "\r\n" << str2 << std::endl;

在这里插入图片描述

3 相关函数 字母含义 解释

     //CT2A含义
	//C:convert,转换的意思
	//T:中间类型,如果定义了_UNICODE,则T表示W;如果定义了_MBCS,则T表示A
	//W:宽字符串,也就是UNICODE
	//A:ANSI字符串,也就是Muti-Byte。

string转CString:CA2T,将多字符集转换为宽字符UNICODE,也可写成CA2W。
CString转string:CT2A,将Unicode转换为多字符集ASCII , 也可写成CW2A。

c_str()就是将C++的string转化为C的字符串数组c_str()生成一个const char *指针,指向字符串的首地址

  • 使用Unicode字符集时,CString等价于CStringW;
  • 使用多字节字符集时,CString相对于CStringA;
1)TCHAR 转换为const wchar_t *,直接强制转换,在TCHAR前面加上(*const wchar_t)

2)BSTR:是一个OLECHAR*类型的Unicode字符串,是一个COM字符串,带长度前缀,与VB有关,没怎么用到过。 

LPSTR:即 char *,指向以'/0'结尾的8位(单字节)ANSI字符数组指针 

LPWSTR:即wchar_t *,指向'/0'结尾的16位(双字节)Unicode字符数组指针 

 LPCSTR:即const char * 

 LPCWSTR:即const wchar_t * 

LPTSTR:LPSTR、LPWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI 

LPCTSTR: LPCSTR、LPCWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI,

如下是从MFC库中拷来的:

#ifdef UNICODE 
typedef LPWSTR LPTSTR; 
typedef LPCWSTR LPCTSTR;
#else 
typedef LPSTR LPTSTR; 
typedef LPCSTR LPCTSTR; 
#endif

相互转换方法:

LPWSTR->LPTSTR:   W2T();
LPTSTR->LPWSTR:   T2W(); 
LPCWSTR->LPCSTR:  W2CT();
LPCSTR->LPCWSTR:  T2CW();
ANSI->UNICODE:   A2W();
UNICODE->ANSI:   W2A();

在头文件<atlconv.h>中定义了ATL提供的所有转换宏,如:

  A2CW       (LPCSTR)  -> (LPCWSTR) 
  A2W        (LPCSTR)  -> (LPWSTR) 
  W2CA       (LPCWSTR) -> (LPCSTR) 
  W2A        (LPCWSTR) -> (LPSTR)

所有的宏如下表所示:
在这里插入图片描述

上表中的宏函数,非常的有规律,每个字母都有确切的含义如下:

在这里插入图片描述

CString与string转换

多字节与宽字节

一文搞懂C++和MFC中的字符串,CString和string如何转换

CString与LPCWSTR的转化

4 C++和MFC中的字符串,CString和string转换 原理

在C的时代,还没有字符串类型,我们通过字符数组,或者char* 来处理 “字符串”。

通过在字符串的最后加上一个‘\0’, 来表示字符串的结束。所以没有字符串类型之前,我们总是在处理完字符串之后加个‘\0’,或者数字0(因为‘\0’对应的ASCII码值是0,注意字符‘0’对应的ASCII码值是0x30

到了C++有了类型string,到了MFC 有了 CString, 但是本质还是 char*

4.1 char* 、 const char* 、 CString 的关系

所以我们在转换的时候,就是通过char*进行中转,按照这个思路,我们就能很好理解这个转换的过程。

在此之前,我们还得理解一个概念:

  • 什么是指向常量的指针?其实就是指针指向的一个常量,指针指向的地址中的值不可变。

因为字符串本身规定就是不可变的,所以大多情况下,我们都是用 一个指向常量的指针管理字符串。如:const char* a1; 这就是一个指向常量的指针,对应MFC中的LPCSTR,其中的这个C就是const的意思,这里我们可以看下MFC中的定义,下次就不怕,也不蒙B了:

在这里插入图片描述

好的,言归正传,看看标准C++中的字符串:

 
    cout << "----------string转const char *----------------" << endl;
    const char* a1;
    string s1 = "asdf";
    a1 = s1.c_str(); //应为字符串是不可变的,所以需要一个指向常量的指针,c_str:c中的字符串。
    cout << a1 << endl;
 
    cout << "-------------string转char *---------------" << endl;
    //--这里相当于对了一个步骤,将const char * 转成 char *
    //--于是就请了const_cast这个玩意帮忙,是个模板,看来想去掉const就找他就行!
    char *a2 = const_cast<char *>(s1.c_str()) ;
    cout << a2 << endl;
 
    cout << "----------char*转string----------------" << endl;
    string s2 = a2; //直接赋值即可!(高端的直接兼容低端的)
    cout << s2 << endl;
 
    cout << "----------char*与const char * 的互换----------------" << endl;
    char* aa = "asdf";
    const char* cc = aa;  // 会报一个警告
    aa = const_cast<char *>(cc);
    cout << cc << " " << aa << endl;

以上这段代码,介绍了string到char*的相互转换,以及如何去掉const。

接下来,再看看MFC中的CString:

首先,想将char* 或者 const char* 转成 CString,是直接可以赋值的(和转成string是一样的)

因为所有的字符串底层逻辑都是char*,它可以直接变成其他“字符串类型”。

既然如此,那么string转CString不就是找 char* 打个桥就可以了?

  • string -> CString

(这种方式在Unicode模式和多字节模式下通用)

string str = "123123123123";
CString mfcstr;
 
mfcstr = CString(str.c_str()); 

首先将string变成const char* 然后,通过const char*构造CString,就完成可这次转换。

  • CString -> string

CString转string的时候需要考虑一个问题,就是MFC允许两种编码格式的编程,一种是多字节一种是Unicode,Unicode自己搞了个宽字符TCHAR,意图是兼容多国语言。所以如果你用Unicode char* 就不再是底层逻辑了,底层变成TCHAR*了。所以,这也是MFC搞死人的地方。

现在建议直接选多字节,有了UTF8的出现,多字节也能支持中文了。

4.2 字符串的历史

这里简单的讲一下,字符串的历史,帮助大家加深理解:

由于计算机是美国人发明的,因此最早只有127个字母被编码到计算机中,也就是大小写英文字母、数字和一些符号,这个编码表称为ASCII编码。

例如:大写字母A的编码是65,小写字母z的编码时122。

要处理中文,显然一个字节是不够的,至少需要两个字节,且不能和ASCII编码冲突,所以我国制定了GB2312编码,用于把中文编进去。

可以想象,全世界上有上百种语言,日本把日文编写到Shift_JIS里,韩国把韩文编写到Euc-kr里,各国有各国的标准,就不可避免出现冲突,结果就是,在多语言混合的文本中就会显示乱码。

在此背景下,Unicode应运而生,Unicode把所有语言都统一到一套编码里,这样就不会有乱码问题了。

Unicode标准在不断发展,最常用的是用两个字节表示一个字符(如果要用到非常生僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。

下面看看ASCII编码和Unicode编码的区别:ASCII编码时1个字节,二Unicode编码通常是两个字节。

那么新的问题就出现了:如果统一成Unicode编码,乱码问题从此消失了,但是写的文本基本上全部是英文时,用Unicode编码比ASCII编码多一倍存储空间,在存储和传输上十分不划算。

本着节约的精神,又出现了把Unicode编码转化成为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1~6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4~6个字节。如果你要传输的文本包含大量英文字母,用UTF-8编码就会节省空间。

各编码方式的比较

由上表可知,UTF-8编码有一个额外的好处,就是ASCII编码实际上可看成是UTF-8编码的一部分,所以只支持ASCII编码的大量历史遗留软件可在UTF-8编码下继续使用。

GetBuffer和GetString目前(VS2022下测试)我发现不管在Unicode还是多字节环境下都只能返回TCHAR了,没法返回char了。

但是我又发现了一种,兼容Unicode和多字节的转换方式: CW2A。

 
CString mfcstr = "456487894564啊士大夫十大";
str = CW2A(mfcstr);

4.3 MFC的字符串转换小结

那么MFC的字符串转换小结就是:

str = CW2A(mfcstr); // CString -> string

mfcstr = CString(str.c_str()); // string -> CString

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R-G-B

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值