如何将CString和std :: string std :: wstring相互转换?


77

CString非常方便,同时std::string与STL容器更兼容。我正在使用hash_map。但是,hash_map不支持将CStrings作为键,因此我想将转换CStringstd::string

编写CString哈希函数似乎需要很多时间。

CString -----> std::string

我怎样才能做到这一点?

std::string -----> CString:

inline CString toCString(std::string const& str)
{
    return CString(str.c_str()); 
}

我对吗?


编辑:

这里有更多问题:

如何从转换wstringCString,反之亦然?

// wstring -> CString
std::wstring src;
CString result(src.c_str());

// CString -> wstring
CString src;
std::wstring des(src.GetString());

这有什么问题吗?

此外,我该如何转换std::wstringstd::string,反之亦然?


3
我不会这样做...使用两种不同的字符串类型已经很糟糕了,但是每次使用地图进行操作时都必须转换吗?听起来很糟糕。保持一致,并使用std :: string。如果出于某种原因您确实认为CString更好,那么为它定义一个哈希函数,以便您的hash_map可以使用它,这比将代码中的混乱加倍好得多。
GManNickG

4
实际上,如果所有代码都是我自己编写的,那将是一致的,但是有一些开源项目,例如使用了freeimage sqlite。我不能在那里修改代码。
user25749

我回答了一个当代的答案(VS2017 MFC ...自VS2012起)
Amit G.

Answers:


97

根据CodeGuru

CStringstd::string

CString cs("Hello");
std::string s((LPCTSTR)cs);

但是: std::string不能总是从构造LPCTSTR。即,该代码对于UNICODE构建将失败。

正如std::string只能从LPSTR/构造的那样LPCSTR,使用VC ++ 7.x或更高版本的程序员可以利用诸如CT2CA中介的转换类。

CString cs ("Hello");
// Convert a TCHAR string to a LPCSTR
CT2CA pszConvertedAnsiString (cs);
// construct a std::string using the LPCSTR input
std::string strStd (pszConvertedAnsiString);

std::stringCString:(来自Visual Studio的CString常见问题解答...

std::string s("Hello");
CString cs(s.c_str());

CStringT可以从字符或宽字符字符串构造。即它可以从char*(即LPSTR)或wchar_t*LPWSTR)转换。

换句话说,炭专业化(的CStringT),即CStringAwchar_t-specilizationCStringWTCHAR-specializationCString可以从任一构造char或宽字符,空终止(空终止在这里非常重要)字符串来源。
Althoug IInspectable修订了“空终止”部分中的评论

不需要NUL端接
CStringT具有采用显式长度参数的转换构造函数。这也意味着您可以CStringTstd::string带有嵌入NUL字符的对象构造对象。


2
Errr ...不客气:)感谢Siddhartha Rao的详细解释。
VonC

最后一段并不完全正确。NUL-不需要终止。CStringT具有采用显式长度参数的转换构造函数。这也意味着您可以CStringTstd::string带有嵌入NUL字符的对象构造对象。
IInspectable 2013年

@IInspectable好点。我已将您的评论包含在答案中,以提高知名度。
VonC

但是,声明对我真的很有帮助:D
亚历山大·莱昂六世

这个答案是非常有用和解释性的,但是OJ的答案是一个更简单的选择。
cp.engr

36

解决方法是使用std::basic_string<TCHAR>而不是,std::string无论您使用哪种字符设置,它都可以正常工作。


5
为了方便和熟悉,我喜欢输入typedef:typedef std::basic_string<TCHAR> tstring
Mike Caron

6

更effecient转换CStringstd::string使用其中指定的长度的转换。

CString someStr("Hello how are you");
std::string std(somStr, someStr.GetLength());

在紧密循环中,这可以显着提高性能。


2
使用此命令时出现错误:cannot convert parameter 1 from 'CString' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'
Alexander Leon VI

5

如果您想要更像C ++的东西,这就是我所使用的。尽管它取决于Boost,但这只是例外。您可以轻松删除那些仅依赖STL和WideCharToMultiByte()Win32 API调用的内容。

#include <string>
#include <vector>
#include <cassert>
#include <exception>

#include <boost/system/system_error.hpp>
#include <boost/integer_traits.hpp>

/**
 * Convert a Windows wide string to a UTF-8 (multi-byte) string.
 */
std::string WideStringToUtf8String(const std::wstring& wide)
{
    if (wide.size() > boost::integer_traits<int>::const_max)
        throw std::length_error(
            "Wide string cannot be more than INT_MAX characters long.");
    if (wide.size() == 0)
        return "";

    // Calculate necessary buffer size
    int len = ::WideCharToMultiByte(
        CP_UTF8, 0, wide.c_str(), static_cast<int>(wide.size()), 
        NULL, 0, NULL, NULL);

    // Perform actual conversion
    if (len > 0)
    {
        std::vector<char> buffer(len);
        len = ::WideCharToMultiByte(
            CP_UTF8, 0, wide.c_str(), static_cast<int>(wide.size()),
            &buffer[0], static_cast<int>(buffer.size()), NULL, NULL);
        if (len > 0)
        {
            assert(len == static_cast<int>(buffer.size()));
            return std::string(&buffer[0], buffer.size());
        }
    }

    throw boost::system::system_error(
        ::GetLastError(), boost::system::system_category);
}

CW2AEX类做了所有为你了。
IInspectable '18

3

(从VS2012开始,至少在VS2017 v15.8.1之前)

由于它是MFC项目,而CString是MFC类,因此MS提供了技术说明TN059:使用MFC MBCS / Unicode转换宏和通用转换宏:

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

使用:

void Example() // ** UNICODE case **
{
    USES_CONVERSION; // (1)

    // CString to std::string / std::wstring
    CString strMfc{ "Test" }; // strMfc = L"Test"
    std::string strStd = W2A(strMfc); // ** Conversion Macro: strStd = "Test" **
    std::wstring wstrStd = strMfc.GetString(); // wsrStd = L"Test"

    // std::string to CString / std::wstring
    strStd = "Test 2";
    strMfc = strStd.c_str(); // strMfc = L"Test 2"
    wstrStd = A2W(strStd.c_str()); // ** Conversion Macro: wstrStd = L"Test 2" **

    // std::wstring to CString / std::string 
    wstrStd = L"Test 3";
    strMfc = wstrStd.c_str(); // strMfc = L"Test 3"
    strStd = W2A(wstrStd.c_str()); // ** Conversion Macro: strStd = "Test 3" **
}

-

脚注:

(1)为了使转换宏有空间来存储临时长度,必须_convert在每个使用转换宏的函数中声明一个称为的局部变量,该局部变量可以执行此操作。这是通过调用USES_CONVERSION宏来完成的。在VS2017 MFC代码(atlconv.h)中,它看起来像这样:

#ifndef _DEBUG
    #define USES_CONVERSION int _convert; (_convert); UINT _acp = ATL::_AtlGetConversionACP() /*CP_THREAD_ACP*/; (_acp); LPCWSTR _lpw; (_lpw); LPCSTR _lpa; (_lpa)
#else
    #define USES_CONVERSION int _convert = 0; (_convert); UINT _acp = ATL::_AtlGetConversionACP() /*CP_THREAD_ACP*/; (_acp); LPCWSTR _lpw = NULL; (_lpw); LPCSTR _lpa = NULL; (_lpa)
#endif

1
USES_CONVERSION使用ATL 7.0字符串转换宏时不需要。Visual Studio 2003附带了ATL
7.0。– IInspectable

3

什么问题吗

有几个问题:

  • CStringCStringT的模板专业化。根据描述字符类型的BaseType的不同,有两个具体的专业名称CStringA:(使用char)和CStringW(使用wchar_t)。
  • 尽管wchar_t在Windows上无处不在用于存储UTF-16编码的代码单元,但使用起来char却模棱两可。后者通常存储ANSI编码的字符,但也可以存储ASCII,UTF-8甚至二进制数据。
  • 我们不知道(CString通过_UNICODE预处理器符号控制)的字符编码(甚至字符类型),这使问题变得模棱两可。我们也不知道所需的字符编码std::string
  • 在Unicode和ANSI之间进行转换本质上是有损的:ANSI编码只能表示Unicode字符集的子集。

为了解决这些问题,我将假设wchar_t它将存储UTF-16编码的代码单元,并char保留UTF-8八位位组序列。这是您可以做出的唯一合理选择,以确保源字符串和目标字符串保留相同的信息,而不会将解决方案限制于源域或目标域的子集。

以下实现从UTF-8到UTF-16的CStringA/CStringWstd::wstring/std::string映射之间进行转换,反之亦然:

#include <string>
#include <atlconv.h>

std::string to_utf8(CStringW const& src_utf16)
{
    return { CW2A(src_utf16.GetString(), CP_UTF8).m_psz };
}

std::wstring to_utf16(CStringA const& src_utf8)
{
    return { CA2W(src_utf8.GetString(), CP_UTF8).m_psz };
}

其余两个函数从MFC字符串构造C ++字符串对象,而编码保持不变。请注意,尽管以前的功能无法应付嵌入的NUL字符,但这些功能不受此影响。

#include <string>
#include <atlconv.h>

std::string to_std_string(CStringA const& src)
{
    return { src.GetString(), src.GetString() + src.GetLength() };
}

std::wstring to_std_wstring(CStringW const& src)
{
    return { src.GetString(), src.GetString() + src.GetLength() };
}

1

这是Sal的回答的后续活动,他/她提供了解决方案:

CString someStr("Hello how are you");
std::string std(somStr, someStr.GetLength());

将非典型的C字符串转换为std :: string时,这也很有用

对我来说,一个用例是有一个预分配的char数组(如C-String),但它不是NUL终止的。(即SHA摘要)。上面的语法允许我指定char数组的SHA摘要的长度,以便std :: string不必查找终止的NUL char,该字符可能存在也可能不存在。

如:

unsigned char hashResult[SHA_DIGEST_LENGTH];    
auto value = std::string(reinterpret_cast<char*>hashResult, SHA_DIGEST_LENGTH);

也许,如果您在附有修正案的同时编辑Sal的答案或对Sal的答案发表评论会更好?
Kmeixner

我尝试过...但是stackoverflow并没有授予我进行编辑的能力。
尼尔

1

这很好用:

//Convert CString to std::string
inline std::string to_string(const CString& cst)
{
    return CT2A(cst.GetString());
}

1

从这篇文章中(谢谢Mark Ransom

将CString转换为字符串(VC6)

我已经对此进行了测试,并且效果很好。

std::string Utils::CString2String(const CString& cString) 
{
    std::string strStd;

    for (int i = 0;  i < cString.GetLength();  ++i)
    {
        if (cString[i] <= 0x7f)
            strStd.append(1, static_cast<char>(cString[i]));
        else
            strStd.append(1, '?');
    }

    return strStd;
}

0

为我工作:

std::wstring CStringToWString(const CString& s)
{
    std::string s2;
    s2 = std::string((LPCTSTR)s);
    return std::wstring(s2.begin(),s2.end());
}

CString WStringToCString(std::wstring s)
{
    std::string s2;
    s2 = std::string(s.begin(),s.end());
    return s2.c_str();
}

有效,直到失败。WStringToCString对于源字符串中的任何非ASCII字符,将失败。CStringToWString对于任何非ASCII字符也将失败,从而产生无效的UTF-16代码单元。我知道此解决方案会时不时弹出,但是它一直是错误的,并且将继续是错误的。
IInspectable '18

0

所有其他答案都没有完全解决我所寻找的是CString即时转换而不是将结果存储在变量中的问题。

该解决方案与上述类似,但是我们还需要一步来实例化一个无名对象。我在举例说明。这是我需要的功能,std::string但我有CString

void CStringsPlayDlg::writeLog(const std::string &text)
{
    std::string filename = "c:\\test\\test.txt";

    std::ofstream log_file(filename.c_str(), std::ios_base::out | std::ios_base::app);

    log_file << text << std::endl;
}

当你有一个怎么称呼它CString

std::string firstName = "First";
CString lastName = _T("Last");

writeLog( firstName + ", " + std::string( CT2A( lastName ) ) );     

请注意,最后一行不是直接的类型转换,但我们正在创建一个无名std::string对象并CString通过其构造函数提供via。


0

您可以使用CT2CA

CString datasetPath;
CT2CA st(datasetPath);
string dataset(st);

0

转换CString to std::string。您可以使用这种格式。

std::string sText(CW2A(CSText.GetString(), CP_UTF8 ));

感谢您的第一个答案。请使用代码突出显示:要转换为该代码CStringstd::string可以使用: std::string sText(CW2A(CSText.GetString(), CP_UTF8 ));
Giszmo

-1

如果您希望在其他字符串类型之间轻松转换,那么_bstr_t该类会更合适吗?它支持之间converstion charwchar_tBSTR


2
-1CString已经完成您命名的所有转换。3年前也是如此。毫无疑问地建议使用COM环境中的类型。
IInspectable 2013年

-1

一种有趣的方法是强制CString转换CStringAstring构造函数内部。与之不同,std::string s((LPCTSTR)cs);即使_UNICODE已定义,它也将起作用。但是,如果是这种情况,它将执行从Unicode到ANSI的转换,因此对于超出ASCII字符集的更高Unicode值,这是不安全的。这种转换受_CSTRING_DISABLE_NARROW_WIDE_CONVERSION预处理器定义的约束。https://msdn.microsoft.com/zh-CN/library/5bzxfsea.aspx

        CString s1("SomeString");
        string s2((CStringA)s1);

那不是演员。这是一次转换。CString具有转换构造函数,使用调用线程的当前语言环境。转换是有损的,您可能会使用不再代表来源的字符串来放大显示。是的,这很容易,方便。但也错了。
IInspectable '18

@IInspectable(CStringA)s1是强制转换,因为它是显式转换。您认为这部分是错误的吗?如果这在特定的用例中起作用,那么按照定义,对于那些用例来说,这肯定不是错误的。如果简单方便,那就更好了。因此,您是说由于区域设置正确,将CString强制转换为CStringA并不总是可靠的吗?我特别问“为什么不...”怀疑的程度很高,如果您能提供详细信息,我很感兴趣。我将进行相应的更新,但是只要您了解局限性,您是否会将此方法称为错误方法?
u8it

语言环境是一种限制。更为致命的是,ANSI编码不能代表Unicode规范中可用的所有代码点。这种转换是有损的。您将不可避免地丢失信息。定义_CSTRING_DISABLE_NARROW_WIDE_CONVERSION预处理器符号是安全的选择:这将导致建议的解决方案无法编译。如果了解所有限制,该解决方案甚至都不安全,因为无法强制执行这些要求。
IInspectable

-1

您可以CString自由地投射到const char*,然后将其分配给,std::string如下所示:

CString cstring("MyCString");
std::string str = (const char*)cstring;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.