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

91

CString很方便,但std::string更兼容STL容器。我正在使用hash_map。但是hash_map不支持将CString作为键,因此我想将CString转换为std::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::wstring转换为std::string或者相反呢?


3
我不会这样做...使用两种不同的字符串类型已经够糟糕了,但是每次在 map 中进行操作都需要进行转换吗?听起来很可怕。保持一致,使用 std::string。如果你真的认为 CString 更好,那么为它定义一个哈希函数,这样你的哈希映射就可以使用它了,这比在代码中增加混淆要好得多。 - GManNickG
4
实际上,如果所有的代码都是我自己编写的,那么它们会保持一致性,但是有一些开源项目比如freeimage sqlite是被使用的。我无法修改它们的代码。 - user25749
我回答了一个现代的问题(VS2017 MFC ...自从VS2012以来) - Amit G.
16个回答

109

根据 CodeGuru 的说法:

CString 转换为 std::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的char特化(即CStringA)、wchar_t特化(CStringW)和TCHAR特化(CString)都可以从char或宽字符(空终止符非常重要)字符串源构建。
虽然IInspectable评论中修正了“空终止符”部分:

不需要空终止符
CStringT具有采用显式长度参数的转换构造函数。这也意味着您可以从包含嵌入NUL字符的std::string对象构建CStringT对象。


2
呃...不客气 :) 感谢Siddhartha Rao提供详细的解释。 - VonC
1
最后一段并不完全正确。NUL终止符并非必需。CStringT具有接受显式长度参数的转换构造函数。这也意味着您可以使用嵌入NUL字符的std::string对象构造CStringT对象。 - IInspectable
@IInspectable 说得好。我已经将您的评论包含在答案中,以增加其可见性。 - VonC
But语句对我非常有帮助 :D - Alexander Leon VI
这个答案非常有用和详细,但是OJ的答案是一个更简单的选择。 - cp.engr

37

通过使用 std::basic_string<TCHAR> 而不是 std::string,您可以解决这个问题,并且无论您的字符设置如何,它都应该正常工作。


6
为了方便和熟悉,我希望将其定义为:typedef std::basic_string<TCHAR> tstring - Mike Caron

6

如果你想要更像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);
}

1
CW2AEX类已经为您完成了所有操作。 - IInspectable
CString cstr1("Hello Unicode!"); CW2AEX<128> stdstr1((LPCTSTR)cstr1); 感谢@IInspectable - Behzad Ebrahimi

5

有任何问题吗?

有几个问题:

  • CStringCStringT 的模板特化。根据描述字符类型的 BaseType,有两种具体的特化:CStringA(使用 char)和 CStringW(使用 wchar_t)。
  • 在 Windows 上,虽然 wchar_t 通常用于存储 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() };
}

4

使用指定长度的转换方式将 CString 转换为 std::string 更加高效。

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

在一个紧密的循环中,这会显著提高性能。

5
使用这个代码时出错:无法将参数 1 从 'CString' 转换为 'const std::basic_string<_Elem,_Traits,_Alloc> &' - Alexander Leon VI
你正在使用哪个版本的标准库——boost还是C++11? - Salman Marvasti
我在使用语言版本为C++ 20的Visual Studio 2022时遇到了一个错误,错误信息为“std::basic_string<char, std::char_traits<char>, std::allocator<char>> ::basic_string: none of the 16 overloads could convert all the argument types”。 - ZoomIn

3

自从VS2012...至少到VS2017 v15.8.1版本为止,

由于这是一个MFC项目,而CString是一个MFC类,微软提供了一个技术说明文档TN059: Using MFC MBCS/Unicode Conversion Macros和通用转换宏:

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
在使用ATL 7.0 字符串转换宏时,不需要使用USES_CONVERSION。 ATL 7.0与Visual Studio 2003一起发布。 - IInspectable

2

从这篇文章中(感谢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;
}

2

CString有一个方法GetString(),如果您使用Unicode,则返回LPCWSTR类型,否则返回LPCSTR

在Unicode情况下,您必须通过wstring传递它:

CString cs("Hello");
wstring ws = wstring(cs.GetString());
string s = string(ws.begin(), ws.end());

否则,您可以直接将字符串转换:
CString cs("Hello");
string s = string(cs.GetString());

2

这个很好用:

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

这是关于编程的内容,请将其从英语翻译成中文。只返回翻译后的文本:对于日语字符,此方法效果不佳。 - sergiol

2

要将 CString 转换为 std::string,您可以使用以下格式。

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

感谢您的第一个答案。请使用代码高亮:要将 CString 转换为 std::string,您可以使用以下代码:std::string sText(CW2A(CSText.GetString(), CP_UTF8 )); - Giszmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接