Wie konvertiere ich CString und :: std :: string :: std :: wstring ineinander?


76

CStringist sehr praktisch, während std::stringes besser mit STL-Container kompatibel ist. Ich benutze hash_map. Allerdings hash_mapunterstützt nicht CStringals Schlüssel, so dass ich konvertieren wollen CStringin std::string.

Das Schreiben einer CStringHash-Funktion scheint viel Zeit in Anspruch zu nehmen.

CString -----> std::string

Wie kann ich das machen?

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

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

Habe ich recht?


BEARBEITEN:

Hier sind weitere Fragen:

Wie kann ich konvertieren wstring, CStringmiteinander?

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

Gibt es ein Problem?

Wie kann ich konvertieren std::wstring, std::stringmiteinander?


3
Ich würde das nicht tun ... Es ist schon schlimm genug, zwei verschiedene Zeichenfolgentypen zu verwenden, aber jedes Mal konvertieren zu müssen, wenn Sie etwas mit einer Karte tun? Hört sich schrecklich an. Seien Sie einfach konsistent und verwenden Sie std :: string. Wenn Sie aus irgendeinem Grund wirklich der Meinung sind, dass CString besser ist, definieren Sie eine Hash-Funktion dafür, damit Ihre hash_map sie verwenden kann. Dies ist weitaus besser, als die Verwirrung in Ihrem Code zu verdoppeln.
GManNickG

4
Eigentlich, wenn der gesamte Code von mir selbst geschrieben wurde, ist er konsistent, aber es werden einige OpenSourcing-Projekte wie Freeimage SQLite verwendet. Ich kann dort den Code nicht ändern.
user25749

Ich beantwortete eine zeitgemäße Antwort (VS2017 MFC ... seit VS2012)
Amit G.

Antworten:


97

Laut CodeGuru :

CStringzu std::string:

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

ABER: std::string kann nicht immer aus a konstruieren LPCTSTR. Das heißt, der Code schlägt für UNICODE-Builds fehl.

Da std::stringnur aus LPSTR/ erstellt werden kann LPCSTR, kann ein Programmierer, der VC ++ 7.x oder besser verwendet, Konvertierungsklassen verwenden, z. B. CT2CAals Vermittler.

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::stringzuCString : (Aus den CString-FAQs von Visual Studio ... )

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

CStringTkann sowohl aus Zeichenfolgen als auch aus Zeichenfolgen mit breiten Zeichen bestehen. dh es kann von char*(dh LPSTR) oder von wchar_t*( LPWSTR) konvertieren .

Mit anderen Worten, char-Spezialisierung (von CStringT) , dh CStringA, wchar_t-specilization CStringWund TCHAR-specialization CStringkann entweder konstruiert werden charoder Breitzeichen,null terminiert (null terminierung ist hier sehr wichtig)String-Quellen.
Trotzdem ändert IInspectable den Teil "Null-Kündigung" in den Kommentaren :

Eine NUL-Kündigung ist nicht erforderlich .
CStringThat Konvertierungskonstruktoren, die ein explizites Längenargument verwenden. Dies bedeutet auch, dass Sie CStringTObjekte aus std::stringObjekten mit eingebetteten NULZeichen erstellen können .


2
Ähm ... du bist willkommen :) Danke an Siddhartha Rao für die detaillierten Erklärungen.
VonC

Der letzte Absatz ist nicht ganz richtig. NUL-Kündigung ist nicht erforderlich. CStringThat Konvertierungskonstruktoren, die ein explizites Längenargument verwenden. Dies bedeutet auch, dass Sie CStringTObjekte aus std::stringObjekten mit eingebetteten NULZeichen erstellen können .
Unsichtbarer

@ IInspectable guter Punkt. Ich habe Ihren Kommentar zur besseren Sichtbarkeit in die Antwort aufgenommen.
VonC

Die Aber-Aussage war wirklich hilfreich für mich: D
Alexander Leon VI

Diese Antwort ist sehr nützlich und erklärend, aber die Antwort von OJ ist eine einfachere Alternative.
cp.engr

36

Lösen Sie das, indem Sie std::basic_string<TCHAR>anstelle von verwenden, std::stringund es sollte unabhängig von Ihrer Charaktereinstellung einwandfrei funktionieren.


5
Ich schreibe das gerne aus Bequemlichkeits- und Vertrautheitsgründen:typedef std::basic_string<TCHAR> tstring
Mike Caron

6

Es ist effizienter, CStringauf std::stringdie Konvertierung zu konvertieren , bei der die Länge angegeben ist.

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

In einer engen Schleife führt dies zu einer signifikanten Leistungsverbesserung.


2
Ich habe einen Fehler dabei:cannot convert parameter 1 from 'CString' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'
Alexander Leon VI

5

Wenn Sie etwas C ++ - ähnlicheres wollen, verwende ich dieses. Obwohl es von Boost abhängt, gibt es nur Ausnahmen. Sie können diejenigen, die es verlassen, leicht entfernen, um nur von der STL und dem WideCharToMultiByte()Win32-API-Aufruf abhängig zu sein .

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

Die CW2AEX- Klasse erledigt das alles bereits für Sie.
Unsichtbarer

3

(Seit VS2012 ... und mindestens bis VS2017 v15.8.1)

Da es sich um ein MFC-Projekt handelt und CString eine MFC-Klasse ist, bietet MS einen technischen Hinweis TN059: Verwenden von MFC-MBCS / Unicode-Konvertierungsmakros und generischen Konvertierungsmakros:

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

Verwenden:

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

- -

Fußnoten:

(1) Damit die Konvertierungsmakros Platz zum Speichern der temporären Länge haben, _convertmuss in jeder Funktion, die die Konvertierungsmakros verwendet , eine lokale Variable deklariert werden , die dies ausführt. Dies erfolgt durch Aufrufen des USES_CONVERSIONMakros. Im VS2017-MFC-Code (atlconv.h) sieht es folgendermaßen aus:

#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_CONVERSIONist bei Verwendung von ATL 7.0- Zeichenfolgenkonvertierungsmakros nicht erforderlich . ATL 7.0 wird mit Visual Studio 2003 ausgeliefert.
Unsichtbarer

3

Gibt es ein Problem?

Es gibt mehrere Probleme:

  • CStringist eine Vorlagenspezialisierung von CStringT . Abhängig vom BaseType , der den Zeichentyp beschreibt, gibt es zwei konkrete Spezialisierungen: CStringA(using char) und CStringW(using wchar_t).
  • Während wchar_tunter Windows allgegenwärtig zum Speichern von UTF-16-codierten Codeeinheiten verwendet wird, ist die Verwendung nicht chareindeutig. Letzteres speichert üblicherweise ANSI-codierte Zeichen, kann aber auch ASCII-, UTF-8- oder sogar Binärdaten speichern.
  • Wir kennen die Zeichenkodierung (oder sogar den Zeichentyp) von CString(die über das _UNICODEPräprozessorsymbol gesteuert wird ) nicht, was die Frage mehrdeutig macht. Wir kennen auch nicht die gewünschte Zeichenkodierung von std::string.
  • Die Konvertierung zwischen Unicode und ANSI ist von Natur aus verlustbehaftet: Die ANSI-Codierung kann nur eine Teilmenge des Unicode-Zeichensatzes darstellen.

Um diese Probleme zu beheben, gehe ich davon aus, dass wchar_tUTF-16-codierte Codeeinheiten gespeichert werden und charUTF-8-Oktettsequenzen enthalten. Dies ist die einzig vernünftige Wahl, die Sie treffen können, um sicherzustellen, dass Quell- und Zielzeichenfolgen dieselben Informationen beibehalten, ohne die Lösung auf eine Teilmenge der Quell- oder Zieldomänen zu beschränken.

Die folgenden Implementierungen konvertieren zwischen CStringA/ CStringWund std::wstring/ std::stringMapping von UTF-8 nach UTF-16 und umgekehrt:

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

Die verbleibenden zwei Funktionen erstellen C ++ - Zeichenfolgenobjekte aus MFC-Zeichenfolgen, wobei die Codierung unverändert bleibt. Beachten Sie, dass die vorherigen Funktionen zwar nicht mit eingebetteten NUL-Zeichen umgehen können, diese Funktionen jedoch dagegen immun sind.

#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

Dies ist eine Fortsetzung von Sal's Antwort, in der er / sie die Lösung lieferte:

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

Dies ist auch nützlich, wenn ein nicht typischer C-String in einen std :: string konvertiert wird

Ein Anwendungsfall für mich war ein vorab zugewiesenes char-Array (wie C-String), das jedoch nicht NUL-beendet ist. (dh SHA Digest). Mit der obigen Syntax kann ich die Länge des SHA-Digests des char-Arrays angeben, damit std :: string nicht nach dem terminierenden NUL-Zeichen suchen muss, das möglicherweise vorhanden ist oder nicht.

Sowie:

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

Vielleicht wäre es besser, wenn Sie Sal's Antwort mit Ihrem Änderungsantrag bearbeiten oder Sal's Antwort kommentieren würden?
Kmeixner

Ich habe es versucht ... aber Stackoverflow hat mir nicht die Möglichkeit gegeben, dies zu tun und zu bearbeiten.
Neil

1

Das funktioniert gut:

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

1

von diesem Beitrag (Danke Mark Ransom )

CString in String konvertieren (VC6)

Ich habe dies getestet und es funktioniert gut.

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

Funktioniert bei mir:

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();
}

Funktioniert, bis es fehlschlägt. WStringToCStringschlägt für alle Nicht-ASCII-Zeichen in der Quellzeichenfolge fehl. CStringToWStringschlägt auch für Nicht-ASCII-Zeichen fehl und erzeugt ungültige UTF-16-Codeeinheiten. Ich verstehe, dass diese Lösung immer wieder auftaucht, aber sie war immer falsch und wird auch weiterhin falsch sein.
Unsichtbarer

0

Alle anderen Antworten haben nicht ganz das angesprochen, wonach ich gesucht habe, nämlich im laufenden Betrieb zu konvertieren CString, anstatt das Ergebnis in einer Variablen zu speichern.

Die Lösung ist ähnlich wie oben, aber wir benötigen einen weiteren Schritt, um ein namenloses Objekt zu instanziieren. Ich illustriere mit einem Beispiel. Hier ist meine Funktion, die std::stringaber ich brauche 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;
}

Wie nennt man es, wenn man eine hat CString?

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

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

Beachten Sie, dass die letzte Zeile keine direkte Typumwandlung ist, sondern dass wir ein namenloses std::stringObjekt erstellen und das CStringüber seinen Konstruktor bereitstellen .


0

Sie können CT2CA verwenden

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

0

konvertieren CString to std::string. Sie können dieses Format verwenden.

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

Danke für deine erste Antwort. Bitte verwenden Sie Code - Hervorhebung: Zur Umrechnung CStringauf std::stringSie diese verwenden können: std::string sText(CW2A(CSText.GetString(), CP_UTF8 ));.
Giszmo

-1

Wenn Sie problemlos zwischen anderen Zeichenfolgentypen konvertieren _bstr_tmöchten , ist die Klasse möglicherweise besser geeignet. Es unterstützt converstion zwischen char, wchar_tund BSTR.


2
-1 führt CStringbereits alle von Ihnen benannten Konvertierungen durch. Und das auch vor 3 Jahren. Es macht keinen Sinn, einen Typ vorzuschlagen, der für die Verwendung in COM-Umgebungen vorgesehen ist.
Unsichtbarer

-1

Ein interessanter Ansatz besteht darin CString, CStringAin einen stringKonstruktor zu werfen . Im Gegensatz std::string s((LPCTSTR)cs);dazu funktioniert dies auch dann, wenn _UNICODEes definiert ist. In diesem Fall wird jedoch eine Konvertierung von Unicode nach ANSI durchgeführt, sodass höhere Unicode-Werte über den ASCII-Zeichensatz hinaus nicht sicher sind. Eine solche Konvertierung unterliegt der _CSTRING_DISABLE_NARROW_WIDE_CONVERSIONPräprozessordefinition. https://msdn.microsoft.com/en-us/library/5bzxfsea.aspx

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

Das ist keine Besetzung. Es ist eine Bekehrung. CStringverfügt über Konvertierungskonstruktoren, die das aktuelle Gebietsschema des aufrufenden Threads verwenden. Die Konvertierung ist verlustbehaftet, und Sie können ein Fenster mit einer Zeichenfolge öffnen, die nicht mehr die Quelle darstellt. Ja, es ist einfach und bequem. Aber auch falsch.
Unsichtbarer

@IInspectable (CStringA)s1ist eine Besetzung in dem Sinne, dass es sich um eine explizite Konvertierung handelt. Ist das der Teil, den Sie hier für falsch halten? Wenn dies in bestimmten Anwendungsfällen funktioniert, was auch der Fall ist, kann es per Definition für diese Anwendungsfälle nicht falsch sein. Wenn es einfach und bequem ist, dann umso besser. Sie sagen also, dass das Umwandeln von CString in CStringA aufgrund des korrekten Gebietsschemas nicht immer zuverlässig ist? Ich habe speziell gefragt, "warum nicht ...", und ich bin interessiert, ob Sie Details angeben können. Ich werde entsprechend aktualisieren, aber würden Sie diesen Ansatz als falsch bezeichnen, solange die Einschränkungen verstanden werden?
U8it

Das Gebietsschema ist eine Einschränkung. Das schwerwiegendere ist, dass die ANSI-Codierung nicht alle in der Unicode-Spezifikation verfügbaren Codepunkte darstellen kann. Diese Umwandlung ist verlustbehaftet. Sie werden unweigerlich Informationen verlieren. Das Definieren des _CSTRING_DISABLE_NARROW_WIDE_CONVERSIONPräprozessorsymbols ist die sichere Option: Diese vorgeschlagene Lösung kann nicht kompiliert werden. Diese Lösung ist nicht einmal sicher, wenn alle Einschränkungen verstanden werden, da es keine Möglichkeit gibt, die Anforderungen durchzusetzen.
Unsichtbarer

-1

Sie können CStringfrei werfen const char*und es dann einem solchen zuweisen std::string:

CString cstring("MyCString");
std::string str = (const char*)cstring;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.