常用字符串转换

 1、std::wstring--string

方法一:

string CwxPropPageAuthenticationSheet::WstingToChar(std::wstring& wstr)  
{  
	string result;  
	//获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的  
	int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);  
	char* buffer = new char[len + 1];  
	//宽字节编码转换成多字节编码  
	WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);  
	buffer[len] = '\0';  
	//删除缓冲区并返回值  
	result.append(buffer);  
	delete[] buffer; 

	return result;
}  
#include 
//将string转换成wstring  
wstring string2wstring(string str)  
{  
    wstring result;  
    //获取缓冲区大小,并申请空间,缓冲区大小按字符计算  
    int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);  
    TCHAR* buffer = new TCHAR[len + 1];  
    //多字节编码转换成宽字节编码  
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);  
    buffer[len] = '\0';             //添加字符串结尾  
    //删除缓冲区并返回值  
    result.append(buffer);  
    delete[] buffer;  
    return result;  
}  
  
//将wstring转换成string  
string wstring2string(wstring wstr)  
{  
    string result;  
    //获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的  
    int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);  
    char* buffer = new char[len + 1];  
    //宽字节编码转换成多字节编码  
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);  
    buffer[len] = '\0';  
    //删除缓冲区并返回值  
    result.append(buffer);  
    delete[] buffer;  
    return result;  
} 

方法二:

#include   
#pragma comment(lib, "comsuppw.lib")
 
string ws2s(const wstring& ws)
{
    _bstr_t t = ws.c_str();  
    char* pchar = (char*)t;  
    string result = pchar;  
    return result;  
}
 
wstring s2ws(const string& s)
{
    _bstr_t t = s.c_str();  
    wchar_t* pwchar = (wchar_t*)t;  
    wstring result = pwchar;  
    return result; 
} 

第1章 C++常见字符串类型介绍

C++中常会看到如char*、wchar_t*、LPSTR、LPCSTR、LPTSTR、LPCTSTR这样的字符串类型,也有CString、CStringA、CStringW、std::string、std::wstring这样的C++类,纷繁复杂难以理解,它们之间的互相转换也如一个个谜团一般。

下面就一起来剥茧抽丝,解开这些谜团。

1.1基本字符串类型

1.1.1字符指针char*

这是大家最常见的类型了,它指向一个字符串的首地址。

根据C/C++的约定,它也能用来表示整个字符串,串的结尾符为'\0'(内存中数值为0)。

char* pString = "字符串内容";

 

1.1.2字符数组char[]

字符数组变量既包含了字符串的首地址信息,在某些场景下可等同于字符指针,所以它能这样:

char szString[] = "字符串内容";

char* pString = szString;      //获取字符数组首地址

 

同时它又包含了整个字符数组的长度信息,所以字符数组也能这样:

size_t nSize = sizeof(szString);  // 获取字符串长度

 

1.1.3 const char*

若一个字符串内容不希望被随意修改,我们应当为其加上const属性,特别是在传入参数时

1.1.4 Unicode(wchar_t类型)

对于Unicode(双字节字符集)字符串,在Windows中使用wchar_t*来表示。在早期的Visual Studio(2005以前)中,wchar_t实际上是unsigned short的typedef。在后期版本中已经称为一种独立的内置类型。

1.1.5 CHAR、WCHAR、TCHAR

CHAR实际就是char,WCHAR就是wchar_t。

TCHAR则使用了宏定义技术来检测项目的字符集配置,根据这个配置来自动适应——当项目是多字节(或ANSI,下同)时,它就是char;项目是宽字节(Unicode,下同)时,它就是wchar_t

#ifdef UNICODE

    typedef WCHAR TCHAR;

#else

    typedef CHAR TCHAR;

#endif

 

1.2 Windows Types(LPSTR系列)

Windows API设计得很精巧,但常常也把简单的东西变得更复杂,下面几种类型其实就是char*、wchar_t*类型的typedef而已。

1.2.1 LPSTR——char*

typedef char* LPSTR;

 

1.2.2 LPCSTR——const char*

typedef const char* LPCSTR;

 

1.2.3 LPWSTR——wchar_t*

typedef wchar_t* LPWSTR;

 

1.2.4 LPCWSTR——const wchar_t*

typedef const wchar_t* LPCWSTR;

 

1.2.5 LPTSTR——TCHAR*

typedef TCHAR* LPTSTR;

 

1.2.6 LPCTSTR——const TCHAR*

typedef const TCHAR* LPCTSTR;

 

1.3 STL字符串(std::string)

该类型来自于STL库,其实所有STL容器都是使用泛型技术的类模版实现,string也不例外,它是类模版basic_string的实例化。本质上跟vector没啥区别,只是为了字符串操作便利性做了许多优化设计而已。

1.3.1 std::string

typedef basic_string<char, …> string;

 

专门针对ANSI字符串的相关操作。

1.3.2 std::wstring

typedef basic_string<wchar_t, …> wstring;

 

专门针对Unicode字符串的相关操作。

1.4 ATL字符串(CString)

1.4.1 BSTR

typedef LPWSTR BSTR;

 

1.4.2 CString

CString也是类模版CStringT的实例化。客观来讲,CString设计上比std::string要更为精巧,也更加实用。

typedef CStringT CString;

 

CString会根据TCHAR的实际类型自动适应项目字符集配置。

1.5  COM字符串bstr_t

bstr_t是COM中的字符串封装类,只是单纯的类,而不是类模版,它在一个类中同时实现了对char*和wchar_t*的支持。

第2章 字符串类型之间的转换

不同的字符串类型设计思路和背景各不相同,互不兼容,但它们一定都兼容const char*、const wchar_t*。所以我们的思路一般是将const char*Unicode的话就是const wchar_t*)作为桥梁,按源类型àconst char*à目标类型来完成转换

2.1 char*到其他类型

2.1.1转换到LPSTR系列

由于char*本身就是LPSTR,所以没有区别,不用转换。

char* pString = "字符串内容";

LPSTR lpStr = pString;

 

 

而非const对象也可以直接转换到const对象,所以也能隐式转换到LPCSTR类型。

char* pString = "字符串内容";

 LPCSTR lpcStr = pString;

 

 

只是需要注意const char*无法直接赋值到LPSTR类型,最安全的方法是将const char*拷贝到一个字符数组,然后再将字符数组赋值给LPSTR类型。

const char* pszString = "字符串内容";

 size_t nBufferSize = ::strlen(pszString) + 1;

 char* pBuffer = new char[nBufferSize];

 ::memset(pBuffer, 0, nBufferSize);

 LPSTR pStr = pBuffer;

 

 

当然我们也能强制转换,但并非安全的做法。

pStr = (char*)pszString;               // C风格

 pStr = const_cast(pszString);    // C++风格

 

 

 

2.1.2 转换到std::string

std::string有针对const char*类型的构造函数和赋值函数,所以char*/const char*能很轻松转换到std::string类型。

// 拷贝构造

char* pString = "字符串内容";

std::string str = pString;

// 赋值

 std::string str1;

 str1 = pString;

 

 

注意当char*的值是NULL时,直接传递到std::string会报告异常,这点是std::string设计得不好的地方。解决方法是使用前先行判断char*的值,若为NULL则不要赋值,简洁的写法如下:

std::string s = lpString != NULL ? lpString : "";

 

2.1.3 转换到BSTR

由于BSTR是wchar_t*的变体,所以这实际上是ANSI到Unicode的转换。这方面内容请参考第3章。后续内容也不在赘述。

2.1.4 转换到CString

char*转换到CString也类似于std::string。

更强的是,CString内部实现了ANSI/Unicode的相互转换,所以char*和wchar_t*类型赋值到CString可以不用考虑字符集的转换。

char* pString = "字符串内容";

wchar_t* pWstring = L"字符串内容";

CString str = pString;

str = pWstring;

 

 

2.1.5转换到bstr_t

bstr_t重载了针对const char*的

2.1.6 wchar*的转换

初学者常见的错误是将char*类型给强制转换到wchar_t*类型,或反过来干。字符编码不同,这样的强制转换是没有意义的,最后只能得到一堆乱码。

// 错误!得到乱码结果!

 char* pString = "字符串内容";

 wchar_t* pWstring = (wchar_t*)pString;

 

 

标准做法是使用MultiByteToWideChar / mbstowcs 从char*转换到wchar_t*,使用WideCharToMultiByte / wcstombs从wchar_t*转换到char*。具体内容可参考第3章。

2.2 LPSTR到其他类型

由于LPSTR系列本来就是char*的变体,所以转换方式可参考char*的转换。以后章节也不在针对LPSTR赘述。

2.3 std::string到其他类型

­2.3.1 到char*

std::string不建议直接转换到char*,但是我们有函数可以直接转换到const char*。

std::string str;

const char* pstr = str.c_str();

 

 

2.3.2 到CString

先转换到const char*,然后再将const char*转换到CString,完事儿。

string str;

CString cstr = str.c_str();

 

 

CString比较强悍的是可以同时处理ANSI和Unicode两种字符集,所以下面这样也完全没问题。

wstring wstr;

CString cstr = wstr.c_str();

 

 

2.3.3 到bstr_t

转换思路跟上一节相同。

string str;

bstr_t bstr = str.c_str();

 

 

2.4 CString到其他类型

2.4.1 到char*

CString实现了operator LPCTSTR(),该类型随项目字符集设置而变化,在多字节环境下是const char*,而在宽字节环境下又变成了const wchar_t*。

所以多字节时,我们像这样转换:

CString str;

const char* pstr = str;

pstr = str.GetString();          // 显式调用成员函数

char* pstr1 = str.GetBuffer();    // 该成员函数返回 char*

 

 

而宽字节环境得显式地利用CStringA:

CString str;

CStringA strA(str);

const char* pstr = strA;

char* pstr1 = strA.GetBuffer();

 

 

2.4.2 到std::string

同样需要区分多字节还是宽字节环境。

多字节环境下,可以直接这样写:

CString cstr = _T("字符串内容");

string str = cstr.GetString();

str = cstr;    // 也同样合法

void Func(std::string );

// 错误!引发C2664

Func(cstr);

// 正确!帮助编译器识别对象类型

Func((const char*)cstr);

Func(cstr.GetString());

 

 

宽字节环境下,同样得利用CStringA做一层转换:

CString cstr = _T("字符串内容");

CString cstrA(str);

string str = cstrA.GetString();

void Func(std::string );

// 错误!引发C2664

Func(cstrA);

// 正确!帮助编译器识别类型

Func(cstrA.GetString());

 

 

在上面两个例子中可以发现一个共性——CString àconst char*àstd::string这层隐式转换关系虽然看起来很明显,而且大多数时候都行得通。但到了函数参数传递时往往就卡壳了。编译器显得很傻,不能顺畅地直接转换过去,需要我们显式地强制转换,或调用对象成员函数,将对象先转换成更基本的const char*类型,从而帮助编译器做好类型识别。

2.4.3到bstr_t

因为bstr_t能同时接受ANSI、Unicode字符串,所以CString到bstr_t的转换又相对简单了:

CString cstr;

bstr_t bstr = cstr;

void Func(bstr_t );

// 错误!C2664

Func(cstr);

// 正确!函数参数传递时注意帮助编译器决断对象类型

Func(cstr.GetString());

 

 

 

2.5  bstr_t到其他类型

2.5.1 到char*

bstr_t不仅实现了支持const char*、const wchar_t*的构造函数、赋值函数,还实现了operator const char*()、operator const wchar_t*()、operator char*()和operator wchar_t*()。

这意味着它可以与char*、wchar_t*、const char*和const wchar_t*之间完全无障碍地进行转换。我们一般就无视那些转换规则了。

但下面这种情况,会报告一个C2668错误:

void Func(char*);

 void Func(wchar_t*);

 bstr_t b;

 // 引发C2668错误

 Func(b);

 

 

 

该错误比之前弱智的C2664错误更容易理解一些。由于Func同时重载了两种类型,编译器对于要转换到哪种类型无从抉择。这时的标准处理方法是写成

Func((wchar_t*)b));

 

加了一个强制转换符,帮助编译器做出明确的选择。

为什么是(wchar_t*)而不是(char*)呢?因为Windows NT API内部就是使用Unicode实现的,如果我们强制转换到ANSI,Windows内部又得转换到Unicode,影响了效率。

2.5.2 到std::string

一般情况下bstr_t也能无障碍转换到std::string。但下面这种情况可能会报告一个C2664错误:

void Func(const std::string& );

bstr_t b;

// 引发C2664错误

Func(b);

 

 

解决办法跟上一节相同。

2.5.3到CString

又是个让编译器做选择的问题,于是编译器又不干了,这次是涉及到模版推断的C2440错误。解决办法依然跟上两节一样,加一个(wchar_t*)强制转换。

bstr_t bstr;

CString str = (wchar_t*)bstr;

 

第3章 ANSI、Unicode之间的转换

3.1使用Windows API

Windows API提供了MultiByteToWideCharWideCharToMultiByte这两个函数来实现多字节、宽字节之间的转换。

后续所有转换方式,内部实现都是基于Windows API的,所以在Windows下这种方法是首选。

3.1.1 ANSI到Unicode

使用Windows API中的MultiByteToWideChar函数可实现多字节到宽字节的转换,代码如下:

string s = "字符串内容";

int nSize = ::MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, NULL, NULL);

wchar_t* lpBuffer = new WCHAR[nSize];

::memset(lpBuffer, 0, nSize * sizeof(lpBuffer[0]));

::MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, lpBuffer, nSize);

wstring ws = lpBuffer;

delete[] lpBuffer;

// delete之后重置为NULL,避免悬挂指针!

lpBuffer = NULL;

 

 

第一次调用返回转换后wchar_t数组的长度,第二次才填充转换后的字符串内容。

3.1.2 Unicode到ANSI

宽字节到多字节的转换使用另外一个函数,方法类似。

wstring ws = L"字符串内容";

int nSize = ::WideCharToMultiByte(CP_ACP, 0, ws.c_str(), -1, NULL, 0, NULL, NULL);

char* lpBuffer = new CHAR[nSize];

::memset(lpBuffer, 0, nSize * sizeof(lpBuffer[0]));

::WideCharToMultiByte(CP_ACP, 0, ws.c_str(), -1, lpBuffer, nSize, 0, 0);

string s = lpBuffer;

delete[] lpBuffer;

lpBuffer = NULL;

 

 

 

3.2 使用C函数库

C函数库中也提供了mbstowcs和wcstombs两个函数来实现字符集转换。Visual Studio中暴露了两者的源代码,可看到这两个函数内部是调用Windows API来实现转换的。

注意在使用这两个函数前,需要先使用setlocale设置全局locale,使用完之后再设置回来。在多线程环境下可能需要注意加锁保护。

3.2.1 ANSI到Unicode

string s = "字符串内容";

string sCurLocale = ::setlocale(LC_ALL, NULL);

::setlocale(LC_ALL, "chs");

int nSize = :: mbstowcs(NULL, s.c_tr(), 0);

wchar_t* lpBuffer = new WCHAR[nSize + 1];

::memset(lpBuffer, 0, (nSize + 1) * sizeof(lpBuffer[0]));

:: mbstowcs(lpBuffer, s.c_tr(), nSize + 1);

::setlocale(LC_ALL, sCurLocale.c_str());

wstring ws = lpBuffer;

delete[] lpBuffer;

lpBuffer = NULL;

 

3.2.2 Unicode到ANSI

wstring ws = L"字符串内容";

string sCurLocale = ::setlocale(LC_ALL, NULL);

::setlocale(LC_ALL, "chs");

int nSize = :: wcstombs(NULL, ws.c_tr(), 0);

char* lpBuffer = new char[nSize + 1];

::memset(lpBuffer, 0, (nSize + 1) * sizeof(lpBuffer[0]));

:: wcstombs (lpBuffer, ws.c_tr(), nSize + 1);

::setlocale(LC_ALL, sCurLocale.c_str());

string s = lpBuffer;

delete[] lpBuffer;

lpBuffer = NULL;

 

3.3 利用CString

3.3.1 ANSI到Unicode

std::string s = "字符串内容";

CStringW cstrW = s.c_str();

std::wstring ws = cstrW.GetString();

 

3.3.2 Unicode到ANSI

std::wstring ws = L"字符串内容";

CStringA cstrA = ws.c_str();

std::string s = cstrA.GetString();

 

3.4 利用bstr_t

前面提到了bstr_t实现了对ANSI和Unicode的同时支持,所以通过它可以轻松进行字符集的转换。

3.4.1 ANSI到Unicode

string s = "字符串内容";

bstr_t bs = s.c_str();

wstring ws = bs;

 

3.4.2 Unicode到ANSI

wstring ws = L"字符串内容";

bstr_t bs = ws.c_str();

string s = bs;

 

3.5 利用COM内部函数

主要利用COM的两个函数_com_util::ConvertStringToBSTR_com_util::ConvertBSTRToString

但注意这两个函数都返回了堆数组指针,用完之后需要调用方释放!

3.5.1 ANSI到Unicode

std::string s = "字符串内容";

BSTR pws = _com_util::ConvertStringToBSTR(s.c_str());

std::wstring ws = pws;

// 函数分配了一个堆数组,用完之后注意释放!

delete[] pws;

pws = NULL;

 

3.5.2 Unicode到ANSI

std::wstring ws = L"字符串内容";

char* pstr = _com_util::ConvertBSTRToString(ws.c_str());

std::string s = pstr;

// 用完之后注意释放!

delete[] pstr;

pstr = NULL;

 

第4章 字符串使用的几个建议或注意事项

4.1 const类型转换到非const类型要谨慎

const属性指明了变量或对象不能被修改,这在C++中是一种约定,也是一种保护性措施。而强制去除const的转换,破坏了这一约定,让代码变得难读、难维护,也带来安全隐患。

4.2在函数参数中,不用作返回的输入参数,一定要声明为const属性

这一条的意义跟3.1条一样。另外一点,std::string只能返回const char*类型,所以也更方便在代码中大规模使用标准C++代码。

4.3 TCHAR相关类型,切勿在静态/动态库的导出接口中使用

TCHAR相关类型除TCHAR*、const TCHAR*外还包括LPTSTR、LPCTSTR、CString等

由于TCHAR是根据项目字符集配置来的,若DLL库配置为ANSI,而DLL调用方是Unicode,则会产生调用异常;反之亦然。

更关键的是从头文件、lib文件、dll文件无法获得库文件的项目字符集配置。

你可能感兴趣的:(work)