Как конвертировать wstring в строку? - PullRequest
182 голосов
/ 26 января 2011

Вопрос в том, как преобразовать wstring в строку?

У меня есть следующий пример:

#include <string>
#include <iostream>

int main()
{
    std::wstring ws = L"Hello";
    std::string s( ws.begin(), ws.end() );

  //std::cout <<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;
    std::cout <<"std::string =     "<<s<<std::endl;
}

вывод с закомментированной строкой:

std::string =     Hello
std::wstring =    Hello
std::string =     Hello

но только без:

std::wstring =    Hello

Что-то не так в примере?Можно ли выполнить преобразование, как указано выше?

РЕДАКТИРОВАТЬ

Новый пример (с учетом некоторых ответов):

#include <string>
#include <iostream>
#include <sstream>
#include <locale>

int main()
{
    setlocale(LC_CTYPE, "");

    const std::wstring ws = L"Hello";
    const std::string s( ws.begin(), ws.end() );

    std::cout<<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;

    std::stringstream ss;
    ss << ws.c_str();
    std::cout<<"std::stringstream =     "<<ss.str()<<std::endl;
}

Вывод:

std::string =     Hello
std::wstring =    Hello
std::stringstream =     0x860283c

, поэтому поток строк нельзя использовать для преобразования wstring в строку.

Ответы [ 15 ]

276 голосов
/ 22 августа 2013

Как отметил Кубби в одном из комментариев, std::wstring_convert (C ++ 11) предоставляет простое и простое решение (вам нужно #include <locale> и <codecvt>):

std::wstring string_to_convert;

//setup converter
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;

//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
std::string converted_str = converter.to_bytes( string_to_convert );

Я использовал комбинацию wcstombs и утомительное распределение / освобождение памяти, прежде чем столкнулся с этим.

http://en.cppreference.com/w/cpp/locale/wstring_convert

обновление (2013.11.28)

Можно указывать один лайнер (спасибо, Гасс, за ваш комментарий):

std::wstring str = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes("some string");

Функции оболочки могут быть сформулированы так: (Спасибо, ArmanSchwarz за комментарий)

std::wstring s2ws(const std::string& str)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.from_bytes(str);
}

std::string ws2s(const std::wstring& wstr)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.to_bytes(wstr);
}

Примечание: есть некоторые противоречия относительно того, следует ли передавать string / wstring в функции как ссылки или как литералы (из-за C ++ 11 и обновлений компилятора). Я оставлю решение человеку, который его реализует, но это стоит знать.

Примечание: я использую std::codecvt_utf8 в приведенном выше коде, но если вы не используете UTF-8, вам нужно изменить его на соответствующую кодировку, которую вы используете:

http://en.cppreference.com/w/cpp/header/codecvt

118 голосов
/ 23 августа 2012

Решение от: http://forums.devshed.com/c-programming-42/wstring-to-string-444006.html

std::wstring wide( L"Wide" ); 
std::string str( wide.begin(), wide.end() );

// Will print no problemo!
std::cout << str << std::endl;

Остерегайтесь , что здесь вообще происходит преобразование набора символов no .Для этого нужно просто присвоить каждому повторному wchar_t char - усеченное преобразование.Он использует std :: string c'tor :

template< class InputIt >
basic_string( InputIt first, InputIt last,
              const Allocator& alloc = Allocator() );

Как указано в комментариях:

значения 0-127 идентичны практически в каждой кодировкеТаким образом, усечение значений, которые меньше 127, приводит к одному и тому же тексту.Введите китайский символ, и вы увидите ошибку.

-

значения 128-255 кодовой страницы Windows 1252 (по умолчанию Windows English) и значения128-255 юникода в основном одинаковы, поэтому, если вы используете кодовую страницу, большинство из этих символов должны быть усечены до правильных значений.(Я полностью ожидал, что õ и õ будут работать, я знаю, что наш код на работе полагается на это для é, что я скоро исправлю)1031 * Win1252 будет не работать.Это включает , œ, ž, Ÿ, ...

29 голосов
/ 26 января 2011

Вот разработанное решение, основанное на других предложениях:

#include <string>
#include <iostream>
#include <clocale>
#include <locale>
#include <vector>

int main() {
  std::setlocale(LC_ALL, "");
  const std::wstring ws = L"ħëłlö";
  const std::locale locale("");
  typedef std::codecvt<wchar_t, char, std::mbstate_t> converter_type;
  const converter_type& converter = std::use_facet<converter_type>(locale);
  std::vector<char> to(ws.length() * converter.max_length());
  std::mbstate_t state;
  const wchar_t* from_next;
  char* to_next;
  const converter_type::result result = converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next);
  if (result == converter_type::ok or result == converter_type::noconv) {
    const std::string s(&to[0], to_next);
    std::cout <<"std::string =     "<<s<<std::endl;
  }
}

Обычно это работает для Linux, но создает проблемы в Windows.

11 голосов
/ 21 июля 2012

Вместо включения локали и всего прочего, если вы знаете, что для FACT ваша строка конвертируема, просто сделайте это:

#include <iostream>
#include <string>

using namespace std;

int main()
{
  wstring w(L"bla");
  string result;
  for(char x : w)
    result += x;

  cout << result << '\n';
}

Живой пример здесь

7 голосов
/ 26 января 2011

Я считаю, что официальный путь еще впереди codecvt граней (вам нужен какой-то перевод с учетом локали), как в

resultCode = use_facet<codecvt<char, wchar_t, ConversionState> >(locale).
  in(stateVar, scratchbuffer, scratchbufferEnd, from, to, toLimit, curPtr);

или что-то в этом роде, у меня нет работающего кода. Но я не уверен, сколько людей в наши дни используют этот механизм, а сколько просто запрашивают указатели на память и позволяют ICU или какой-либо другой библиотеке обрабатывать кровавые детали.

6 голосов
/ 08 ноября 2011

Вы можете также просто использовать узкий метод фасета ctype:

#include <clocale>
#include <locale>
#include <string>
#include <vector>

inline std::string narrow(std::wstring const& text)
{
    std::locale const loc("");
    wchar_t const* from = text.c_str();
    std::size_t const len = text.size();
    std::vector<char> buffer(len + 1);
    std::use_facet<std::ctype<wchar_t> >(loc).narrow(from, from + len, '_', &buffer[0]);
    return std::string(&buffer[0], &buffer[len]);
}
6 голосов
/ 20 июля 2011

Во время написания этого ответа поисковая система Google номер один для преобразования строки wstring попадет на эту страницу. Мой ответ показывает, как преобразовать строку в wstring, хотя это НЕ фактический вопрос, и я, вероятно, должен удалить этот ответ, но это считается плохой формой. Вы можете перейти к этому ответу StackOverflow, который теперь имеет более высокий рейтинг, чем эта страница.


Вот способ объединения строковых, wstring и смешанных строковых констант в wstring. Используйте класс wstringstream.

#include <sstream>

std::string narrow = "narrow";
std::wstring wide = "wide";

std::wstringstream cls;
cls << " abc " << narrow.c_str() << L" def " << wide.c_str();
std::wstring total= cls.str();
6 голосов
/ 26 января 2011

Есть две проблемы с кодом:

  1. Преобразование в const std::string s( ws.begin(), ws.end() ); не требуется для правильного сопоставления широких символов с их узким аналогом. Скорее всего, каждый широкий символ будет просто приведен к char.
    Решение этой проблемы уже дано в ответе kem и включает в себя функцию narrow фасета ctype локали.

  2. Вы записываете выходные данные для std::cout и std::wcout в одной и той же программе. И cout, и wcout связаны с одним и тем же потоком (stdout) и результатами использования одного и того же потока как в виде байтового потока (как это делает cout), так и в широком ориентированном потоке (как wcout не) не определены.
    Лучший вариант - избегать смешивания узких и широких выходных данных с одним и тем же (базовым) потоком. Для stdout / cout / wcout вы можете попробовать переключить ориентацию stdout при переключении между широким и узким выходом (или наоборот):

    #include <iostream>
    #include <stdio.h>
    #include <wchar.h>
    
    int main() {
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
        fwide(stdout, -1); // switch to narrow
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
    }
    
3 голосов
/ 25 сентября 2018

Кодировка по умолчанию включена:

  • Windows UTF-16.
  • Linux UTF-8.
  • MacOS UTF-8.

Этот код имеет две формы для преобразования std :: string в std :: wstring и std :: wstring в std :: string. Если вы отрицаете # если определен WIN32, вы получите тот же результат.

1. std :: string to std :: wstring

MultiByteToWideChar WinAPI

_mbstowcs_s_l

#if defined WIN32
#include <windows.h>
#endif

std::wstring StringToWideString(std::string str)
{
    if (str.empty())
    {
        return std::wstring();
    }
    size_t len = str.length() + 1;
    std::wstring ret = std::wstring(len, 0);
#if defined WIN32
    int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, &str[0], str.size(), &ret[0], len);
    ret.resize(size);
#else
    size_t size = 0;
    _locale_t lc = _create_locale(LC_ALL, "en_US.UTF-8");
    errno_t retval = _mbstowcs_s_l(&size, &ret[0], len, &str[0], _TRUNCATE, lc);
    _free_locale(lc);
    ret.resize(size - 1);
#endif
    return ret;
}

2. std :: wstring to std :: string

WideCharToMultiByte WinAPI

_wcstombs_s_l

std::string WidestringToString(std::wstring wstr)
{
    if (wstr.empty())
    {
        return std::string();
    }
#if defined WIN32
    int size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &wstr[0], wstr.size(), NULL, 0, NULL, NULL);
    std::string ret = std::string(size, 0);
    WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &wstr[0], wstr.size(), &ret[0], size, NULL, NULL);
#else
    size_t size = 0;
    _locale_t lc = _create_locale(LC_ALL, "en_US.UTF-8");
    errno_t err = _wcstombs_s_l(&size, NULL, 0, &wstr[0], _TRUNCATE, lc);
    std::string ret = std::string(size, 0);
    err = _wcstombs_s_l(&size, &ret[0], size, &wstr[0], _TRUNCATE, lc);
    _free_locale(lc);
    ret.resize(size - 1);
#endif
    return ret;
}

3. В Windows вам нужно распечатать Unicode, используя WinAPI.

WriteConsole

#if defined _WIN32
    void WriteLineUnicode(std::string s)
    {
        std::wstring unicode = StringToWideString(s);
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
        std::cout << std::endl;
    }

    void WriteUnicode(std::string s)
    {
        std::wstring unicode = StringToWideString(s);
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
    }

    void WriteLineUnicode(std::wstring ws)
    {
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
        std::cout << std::endl;
    }

    void WriteUnicode(std::wstring ws)
    {
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
    }

4. По основной программе.

#if defined _WIN32
int wmain(int argc, WCHAR ** args)
#else
int main(int argc, CHAR ** args)
#endif
{
    std::string source = u8"ÜüΩωЙ你月曜日\na?èéøÞǽлљΣæča??";
    std::wstring wsource = L"ÜüΩωЙ你月曜日\na?èéøÞǽлљΣæča??";

    WriteLineUnicode(L"@" + StringToWideString(source) + L"@");
    WriteLineUnicode("@" + WidestringToString(wsource) + "@");
    return EXIT_SUCCESS;
}

5. Наконец, вам нужна мощная и полная поддержка символов Юникода в консоли. Я рекомендую ConEmu и установить в качестве терминала по умолчанию в Windows . Вам нужно подключить Visual Studio к ConEmu. Помните, что исполняемый файл Visual Studio devenv.exe

Протестировано на Visual Studio 2017 с VC ++; станд = C ++ 17.

Результат

Result1

3 голосов
/ 27 апреля 2016

Это решение вдохновлено решением dk123, но использует зависящий от локали фасет codecvt. Результат находится в строке с кодировкой локали вместо utf8 (если она не установлена ​​как локаль):

std::string w2s(const std::wstring &var)
{
   static std::locale loc("");
   auto &facet = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
   return std::wstring_convert<std::remove_reference<decltype(facet)>::type, wchar_t>(&facet).to_bytes(var);
}

std::wstring s2w(const std::string &var)
{
   static std::locale loc("");
   auto &facet = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
   return std::wstring_convert<std::remove_reference<decltype(facet)>::type, wchar_t>(&facet).from_bytes(var);
}

Я искал это, но я не могу его найти. Наконец, я обнаружил, что могу получить правильный фасет из std :: locale, используя функцию std :: use_facet () с правильным именем типа. Надеюсь, это поможет.

...