Як конвертувати CString та std :: string std :: wstring один до одного?


78

CStringє досить зручним, хоча std::stringє більш сумісним із контейнером STL. Я використовую hash_map. Однак hash_mapне підтримує CStrings як клавіші, тому я хочу перетворити CStringa на std::string.

Написання CStringхеш-функції займає багато часу.

CString -----> std::string

Як я можу це зробити?

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

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

Я правий?


РЕДАГУВАТИ:

Ось ще запитання:

Як я можу перетворити з wstringна CStringі навпаки?

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

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

Чи є з цим якісь проблеми?

Крім того, як я можу конвертувати з std::wstringв std::stringі навпаки?


3
Я б цього не зробив ... Використання двох різних типів рядків досить погано, але доводиться конвертувати кожен раз, коли ви щось робите з картою? Звучить жахливо. Просто будьте послідовними і використовуйте std :: string. Якщо з якихось причин ви дійсно вважаєте, що CString кращий, тоді визначте для нього хеш-функцію, щоб ваш hash_map міг її використовувати, це набагато краще, ніж подвоєння заплутаного у вашому коді.
GManNickG

4
Насправді, якщо весь код написаний мною, він буде послідовним, але використовуються такі проекти з відкритим кодом, як freeimage sqlite. Я не можу змінити там код.
user25749

Я відповів на сучасну відповідь (VS2017 MFC ... Since VS2012)
Аміт Г.

Відповіді:


97

Відповідно до 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::stringдоCString : (З поширених запитань про CString у Visual Studio ... )

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

CStringTможе будувати як з символьних, так і з широкосимвольних рядків. тобто він може конвертувати з char*(тобто LPSTR) або з wchar_t*( LPWSTR).

Іншими словами, спеціальна спеціалізація (з CStringT), тобтоCStringA , wchar_t-specilization CStringWі TCHAR-specialization CStringможуть бути виготовлені з будь-якого charабо широкого характеру,нуль припинено (нуль-завершення тут дуже важливо)рядкові джерела.
Althoug IIspectable вносить зміни до частини "припинення нуля" у коментарях :

Припинення NUL не потрібно .
CStringTмає конструктори перетворення, які приймають явний аргумент довжини. Це також означає, що ви можете будувати CStringTоб'єкти з std::stringоб'єктів із вбудованими NULсимволами.


2
Помилка ... ласкаво просимо :) Дякуємо Сіддхарті Рао за докладні пояснення.
VonC

Останній абзац не зовсім правильний. NUL-припинення не потрібно. CStringTмає конструктори перетворення, які приймають явний аргумент довжини. Це також означає, що ви можете будувати CStringTоб'єкти з std::stringоб'єктів із вбудованими NULсимволами.
IIСпецифічно

@IInspectable хороший момент. Я включив ваш коментар у відповідь для більшої наочності.
VonC

Заява «Але» мені справді допомогла: D
Олександр Леон VI

Ця відповідь є дуже корисною та пояснювальною, але відповідь OJ є простішою альтернативою.
cp.engr

36

Вирішіть це, використовуючи std::basic_string<TCHAR>замість, std::stringі це повинно працювати нормально незалежно від налаштування вашого персонажа.


5
Я люблю друкувати це для зручності та знайомства:typedef std::basic_string<TCHAR> tstring
Mike Caron 02

6

Більш ефективно перетворити CStringна std::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> &'
Олександр Леон VI

5

Якщо ви хочете щось більше на С ++, наприклад, я цим користуюся. Хоча це залежить від 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 все це вже робить для вас.
IIНевидимий

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макросу. У коді VFC2017 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 . ATL 7.0, поставляється з Visual Studio 2003.
II, спеціально

3

Чи є якісь проблеми?

Є кілька питань:

  • CStringє спеціалізацією на шаблонах CStringT . Залежно від базового типу, що описує тип символу, існує дві конкретні спеціалізації: 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. Це єдиний розумний вибір, який ви можете зробити, щоб забезпечити збереження однакової інформації в рядках джерела та призначення, не обмежуючи рішення лише підмножиною вихідних або цільових доменів.

Наступні реалізації перетворюють між CStringA/ CStringWта std::wstring/ std::stringвідображення з UTF-8 на UTF-16 і навпаки:

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

Решта дві функції будують рядкові об'єкти C ++ із рядків MFC, залишаючи кодування незмінним. Зауважте, що хоча попередні функції не можуть впоратися із вбудованими символами 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() };
}

2

Це чудово працює:

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

1

Це продовження відповіді Сала, де він / вона надав рішення:

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

Це корисно також при перетворенні нетипової C-String в std :: string

Випадок використання для мене був попередньо виділений масив char (наприклад, C-String), але він не припинявся NUL. (тобто дайджест SHA). Вищезазначений синтаксис дозволяє мені вказати довжину дайджесту SHA масиву char, щоб std :: string не мусив шукати кінцевий символ NUL, який може там бути, а може і не бути.

Як от:

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

Можливо, було б краще, якби ви редагували відповідь Сала із доданою поправкою або коментували відповідь Сала?
Kmeixner

Я спробував ... але stackoverflow не надав мені можливості робити та редагувати.
Ніл,


1

Ви можете використовувати CT2CA

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

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. Я розумію, що це рішення постійно з’являється раз у раз, але воно завжди було неправильним і буде продовжувати бути неправильним.
IIСпецифічно

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через його конструктор.


0

конвертувати CString to std::string. Ви можете використовувати цей формат.

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

Дякую за вашу першу відповідь. Будь ласка , використовуйте код підсвічування: Для того, щоб перетворити CStringв std::stringце можна використовувати: std::string sText(CW2A(CSText.GetString(), CP_UTF8 ));.
Giszmo

-1

Якщо ви хочете легко перетворити між іншими типами рядків, можливо, _bstr_tклас був би доречнішим? Він підтримує converstion між char, wchar_tі BSTR.


2
-1 CStringвже виконує всі перетворення, які ви називаєте. І це було також 3 роки тому. Немає сенсу пропонувати тип, призначений для використання в середовищах COM.
IIНевидимий

-1

Один цікавий підхід полягає в гіпс , CStringщоб CStringAусередині stringконструктора. На відміну від std::string s((LPCTSTR)cs);цього буде працювати, навіть якщо _UNICODEвизначено. Однак, якщо це так, це буде виконувати перетворення з Unicode в ANSI, тому небезпечно для вищих значень Unicode, що перевищують набір символів ASCII. Таке перетворення підлягає _CSTRING_DISABLE_NARROW_WIDE_CONVERSIONвизначенню препроцесора. https://msdn.microsoft.com/en-us/library/5bzxfsea.aspx

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

Це не акторський склад. Це перетворення. CStringмає конструктори перетворення, використовуючи поточну локаль викликаючого потоку. Перетворення є втратним, і ви можете вікно з рядком, який більше не представляє джерело. Так, це легко, зручно. Але також неправильно.
IIНевидимий

@IInspectable (CStringA)s1- це приведення в тому сенсі, що це явне перетворення. Це та частина, яку ви тут вважаєте неправильною? Якщо це працює в конкретних випадках використання, що і відбувається, то за визначенням це не може бути неправильним для цих випадків використання. Якщо це легко і зручно, то тим краще. Отже, ви говорите, що приведення CString до CStringA не завжди є надійним через правильну локаль? Я спеціально запитав "чому б і ні ..." підозрювати стільки ж, і мені цікаво, якщо ви можете надати деталі. Я оновлюся відповідно, але чи не назвали б ви цей підхід неправильним, доки зрозуміли обмеження?
u8it

Локаль - це одне обмеження. Більш фатальним є те, що кодування ANSI не може представляти всі точки коду, доступні в специфікації Unicode. Це перетворення є втратним. Ви неминуче втратите інформацію. Визначення _CSTRING_DISABLE_NARROW_WIDE_CONVERSIONсимволу препроцесора є безпечним варіантом: це призведе до невдалої компіляції. Це рішення навіть не є безпечним, якщо розуміються всі обмеження, оскільки немає можливості забезпечити виконання вимог.
IIСпецифічно

-1

Ви можете CStringвільно транслювати, const char*а потім призначити його std::stringтаким чином:

CString cstring("MyCString");
std::string str = (const char*)cstring;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.