How do you convert CString and std::string std::wstring to each other?
Asked Answered
P

16

93

CString is quite handy, while std::string is more compatible with STL container. I am using hash_map. However, hash_map does not support CStrings as keys, so I want to convert the CString into a std::string.

Writing a CString hash function seems to take a lot of time.

CString -----> std::string

How can I do this?

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

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

Am I right?


EDIT:

Here are more questions:

How can I convert from wstring to CString and vice versa?

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

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

Is there any problem with this?

Additionally, how can I convert from std::wstring to std::string and vice versa?

Phelgon answered 3/11, 2008 at 6:58 Comment(3)
I wouldn't do this... Using two different strings types is bad enough, but having to convert every time you do something with a map? Sounds terrible. Just be consistent and use std::string. If for some reason you really think CString is better, then define an hash function for it so your hash_map can use it, this is far better than doubling the confusing in your code.Unintelligible
Actually, If all code is written by myself it will be consistent, but there are some opensourced project such as freeimage sqlite used. I can not modify there code.Phelgon
I answered a contemporary answer (VS2017 MFC ...Since VS2012)Outage
W
110

According to CodeGuru:

CString to std::string:

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

BUT: std::string cannot always construct from a LPCTSTR. i.e. the code will fail for UNICODE builds.

As std::string can construct only from LPSTR / LPCSTR, a programmer who uses VC++ 7.x or better can utilize conversion classes such as CT2CA as an intermediary.

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::string to CString: (From Visual Studio's CString FAQs...)

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

CStringT can construct from both character or wide-character strings. i.e. It can convert from char* (i.e. LPSTR) or from wchar_t* (LPWSTR).

In other words, char-specialization (of CStringT) i.e. CStringA, wchar_t-specilization CStringW, and TCHAR-specialization CString can be constructed from either char or wide-character, null terminated (null-termination is very important here) string sources.
Althoug IInspectable amends the "null-termination" part in the comments:

NUL-termination is not required.
CStringT has conversion constructors that take an explicit length argument. This also means that you can construct CStringT objects from std::string objects with embedded NUL characters.

Whereabouts answered 3/11, 2008 at 7:5 Comment(5)
Errr... you are welcome :) Thanks to Siddhartha Rao for the detailed explanations.Whereabouts
The last paragraph is not entirely correct. NUL-termination is not required. CStringT has conversion constructors that take an explicit length argument. This also means that you can construct CStringT objects from std::string objects with embedded NUL characters.Margarettemargarida
@Margarettemargarida good point. I have included your comment in the answer for more visibility.Whereabouts
The But statement was really helpful to me :DThurifer
This answer is very useful and explanatory, but OJ's answer is a simpler alternative.Saintpierre
B
37

Solve that by using std::basic_string<TCHAR> instead of std::string and it should work fine regardless of your character setting.

Booma answered 3/11, 2008 at 9:36 Comment(1)
I like to typedef that for convenience and familiarity: typedef std::basic_string<TCHAR> tstringSlightly
D
6

If you want something more C++-like, this is what I use. Although it depends on Boost, that's just for exceptions. You can easily remove those leaving it to depend only on the STL and the WideCharToMultiByte() Win32 API call.

#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);
}
Duodenitis answered 12/7, 2009 at 15:5 Comment(2)
The CW2AEX class does all that for you already.Margarettemargarida
CString cstr1("Hello Unicode!"); CW2AEX<128> stdstr1((LPCTSTR)cstr1); Thank to @MargarettemargaridaUnshakable
M
6

Is there any problem?

There are several issues:

  • CString is a template specialization of CStringT. Depending on the BaseType describing the character type, there are two concrete specializations: CStringA (using char) and CStringW (using wchar_t).
  • While wchar_t on Windows is ubiquitously used to store UTF-16 encoded code units, using char is ambiguous. The latter commonly stores ANSI encoded characters, but can also store ASCII, UTF-8, or even binary data.
  • We don't know the character encoding (or even character type) of CString (which is controlled through the _UNICODE preprocessor symbol), making the question ambiguous. We also don't know the desired character encoding of std::string.
  • Converting between Unicode and ANSI is inherently lossy: ANSI encoding can only represent a subset of the Unicode character set.

To address these issues, I'm going to assume that wchar_t will store UTF-16 encoded code units, and char will hold UTF-8 octet sequences. That's the only reasonable choice you can make to ensure that source and destination strings retain the same information, without limiting the solution to a subset of the source or destination domains.

The following implementations convert between CStringA/CStringW and std::wstring/std::string mapping from UTF-8 to UTF-16 and vice versa:

#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 };
}

The remaining two functions construct C++ string objects from MFC strings, leaving the encoding unchanged. Note that while the previous functions cannot cope with embedded NUL characters, these functions are immune to that.

#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() };
}
Margarettemargarida answered 6/9, 2018 at 10:7 Comment(0)
N
4

It is more efficient to convert CString to std::string using the conversion where the length is specified.

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

In a tight loop, this makes a significant performance improvement.

Nonrestrictive answered 3/6, 2011 at 13:49 Comment(3)
I got an error using this: cannot convert parameter 1 from 'CString' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'Thurifer
which version of std library you are using --- boost or C++ 11?Nonrestrictive
I got an error saying 'std::basic_string<char, std::char_traits<char>, std::allocator<char>>::basic_string: none of the 16 overloads could convert all the argument types'. on Visual Studio 2022 with language version set to C++ 20.Drover
O
3

(Since VS2012 ...and at least until VS2017 v15.8.1)

Since it's a MFC project & CString is a MFC class, MS provides a Technical Note TN059: Using MFC MBCS/Unicode Conversion Macros and Generic Conversion Macros:

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

Use:

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" **
}

--

Footnotes:

(1) In order to for the conversion-macros to have space to store the temporary length, it is necessary to declare a local variable called _convert that does this in each function that uses the conversion macros. This is done by invoking the USES_CONVERSION macro. In VS2017 MFC code (atlconv.h) it looks like this:

#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
Outage answered 21/8, 2018 at 9:14 Comment(1)
USES_CONVERSION is not required when using ATL 7.0 string conversion macros. ATL 7.0 shipped with Visual Studio 2003.Margarettemargarida
M
2

This works fine:

//Convert CString to std::string
inline std::string to_string(const CString& cst)
{
    return CT2A(cst.GetString());
}
Mesne answered 21/1, 2017 at 23:17 Comment(1)
this will not work well for Japanese charactersOstraw
K
2

From this post (Thank you Mark Ransom )

Convert CString to string (VC6)

I have tested this and it works fine.

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;
}
Kingly answered 21/8, 2018 at 8:55 Comment(0)
M
2

to convert CString to std::string. You can use this format.

std::string sText(CW2A(CSText.GetString(), CP_UTF8 ));
Manciple answered 24/10, 2019 at 4:51 Comment(1)
Thanks for your first answer. Please use code highlighting: To convert CString to std::string you can use this: std::string sText(CW2A(CSText.GetString(), CP_UTF8 ));.Resumption
B
2

CString has method, GetString(), that returns an LPCWSTR type if you are using Unicode, or LPCSTR otherwise.

In the Unicode case, you must pass it through a wstring:

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

Else you can simply convert the string directly:

CString cs("Hello");
string s = string(cs.GetString());
Broach answered 22/9, 2021 at 13:23 Comment(0)
E
1

This is a follow up to Sal's answer, where he/she provided the solution:

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

This is useful also when converting a non-typical C-String to a std::string

A use case for me was having a pre-allocated char array (like C-String), but it's not NUL terminated. (i.e. SHA digest). The above syntax allows me to specify the length of the SHA digest of the char array so that std::string doesn't have to look for the terminating NUL char, which may or may not be there.

Such as:

unsigned char hashResult[SHA_DIGEST_LENGTH];    
auto value = std::string(reinterpret_cast<char*>hashResult, SHA_DIGEST_LENGTH);
Eakins answered 10/3, 2016 at 19:4 Comment(2)
Perhaps, it would be better if you edited Sal's answer with your amendment attached or commented on Sal's answer?Cramped
I tried... but stackoverflow hasn't granted me the ability to do and edit.Eakins
I
1

One interesting approach is to cast CString to CStringA inside a string constructor. Unlike std::string s((LPCTSTR)cs); this will work even if _UNICODE is defined. However, if that is the case, this will perform conversion from Unicode to ANSI, so it is unsafe for higher Unicode values beyond the ASCII character set. Such conversion is subject to the _CSTRING_DISABLE_NARROW_WIDE_CONVERSION preprocessor definition. https://msdn.microsoft.com/en-us/library/5bzxfsea.aspx

        CString s1("SomeString");
        string s2((CStringA)s1);
Inductile answered 17/10, 2017 at 19:58 Comment(3)
That's not a cast. It's a conversion. CString has conversion constructors, using the calling thread's current locale. The conversion is lossy, and you may window up with a string that no longer represents the source. Yes, it's easy, convenient. But also wrong.Margarettemargarida
@Margarettemargarida (CStringA)s1 is a cast in the sense that it's an explicit conversion. Is that the part you consider wrong here? If this works in specific use cases, which it does, then by definition it cannot be wrong for those use cases. If it is easy and convenient, then all the better. So you are saying that casting CString to CStringA is not always reliable because of locale correct? I specifically asked "why not..." suspecting as much, and I'm interested if you can provide details. I'll update accordingly, but would you call this approach wrong so long as the limitations are understood?Inductile
Locale is one limitation. The more fatal one being, that ANSI encoding cannot represent all code points available in the Unicode specification. That conversion is lossy. You will inevitably lose information. Defining the _CSTRING_DISABLE_NARROW_WIDE_CONVERSION preprocessor symbol is the safe option: It will cause this proposed solution to fail to compile. This solution isn't even safe if all limitations are understood, as there is no way to enforce the requirements.Margarettemargarida
T
1

You can use CT2CA

CString datasetPath;
CT2CA st(datasetPath);
string dataset(st);
Tribal answered 19/4, 2019 at 17:31 Comment(0)
I
0

All other answers didn't quite address what I was looking for which was to convert CString on the fly as opposed to store the result in a variable.

The solution is similar to above but we need one more step to instantiate a nameless object. I am illustrating with an example. Here is my function which needs std::string but I have 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;
}

How to call it when you have a CString?

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

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

Note that the last line is not a direct typecast but we are creating a nameless std::string object and supply the CString via its constructor.

Idola answered 29/6, 2017 at 21:15 Comment(0)
B
-1

If you're looking to convert easily between other strings types, perhaps the _bstr_t class would be more appropriate? It supports converstion between char, wchar_t and BSTR.

Booma answered 20/2, 2009 at 9:28 Comment(1)
-1 CString already does all the conversions you name. And it did 3 years ago as well. No point in suggesting a type that is meant for use in COM environments.Margarettemargarida
P
-1

Works for me:

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();
}
Prohibitory answered 10/11, 2015 at 10:10 Comment(1)
Works, until it fails. WStringToCString will fail for any non-ASCII character in the source string. CStringToWString will fail for any non-ASCII character as well, producing invalid UTF-16 code units. I understand that this solution keeps popping up every now and again, but it has always been wrong, and it will continue to be wrong.Margarettemargarida

© 2022 - 2024 — McMap. All rights reserved.