Как мне преобразовать wchar_t* в std::string?

Я изменил свой класс, чтобы использовать std::string (основываясь на полученном здесь ответе, но у меня есть функция, возвращающая wchar_t *. Как мне преобразовать его в std::string?

Я попробовал это:

std::string test = args.OptionArg();

но он говорит об ошибке C2440: "инициализация": невозможно преобразовать из "wchar_t *" в "std::basic_string<_Elem, _Traits, _Ax>"

7 ответов

Решение

Вы могли бы просто использовать wstring и держи все в юникоде

wstring ws( args.OptionArg() );
string test( ws.begin(), ws.end() );

Вы можете преобразовать широкую строку символов в строку ASCII, используя следующую функцию:

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

std::string ToNarrow( const wchar_t *s, char dfault = '?', 
                      const std::locale& loc = std::locale() )
{
  std::ostringstream stm;

  while( *s != L'\0' ) {
    stm << std::use_facet< std::ctype<wchar_t> >( loc ).narrow( *s++, dfault );
  }
  return stm.str();
}

Имейте в виду, что это просто заменит любой широкий символ, для которого эквивалентный символ ASCII не существует с dfault параметр; он не конвертируется из UTF-16 в UTF-8. Если вы хотите конвертировать в UTF-8, используйте такую ​​библиотеку, как ICU.

Довольно разочаровывает тот факт, что ни один из ответов на этот старый вопрос не решает проблему преобразования широких строк в строки UTF-8, что важно в средах, отличных от английского.

Вот пример кода, который работает и может быть использован в качестве подсказки для создания пользовательских преобразователей. Он основан на примере кода из примера кода на сайте cppreference.com .

      #include <iostream>
#include <clocale>
#include <string>
#include <cstdlib>
#include <array>

std::string convert(const std::wstring& wstr)
{
    const int BUFF_SIZE = 7;
    if (MB_CUR_MAX >= BUFF_SIZE) throw std::invalid_argument("BUFF_SIZE too small");
    std::string result;
    bool shifts = std::wctomb(nullptr, 0);  // reset the conversion state
    for (const wchar_t wc : wstr)
    {
        std::array<char, BUFF_SIZE> buffer;
        const int ret = std::wctomb(buffer.data(), wc);
        if (ret < 0) throw std::invalid_argument("inconvertible wide characters in the current locale");
        buffer[ret] = '\0';  // make 'buffer' contain a C-style string
        result = result + std::string(buffer.data());
    }
    return result;
}

int main()
{
    auto loc = std::setlocale(LC_ALL, "en_US.utf8");  // UTF-8
    if (loc == nullptr) throw std::logic_error("failed to set locale");
    std::wstring wstr = L"aąß水-扫描-€\u00df\u6c34\U0001d10b";
    std::cout << convert(wstr) << "\n";
}

Это печатает, как и ожидалось:

Объяснение

  • 7 кажется минимальным безопасным значением размера буфера, BUFF_SIZE. Сюда входит 4 как максимальное количество байтов UTF-8, кодирующих один символ; 2 для возможной «последовательности сдвига», 1 для завершающей '\0'.
  • MB_CUR_MAXэто переменная времени выполнения времени выполнения , поэтому static_assertздесь нельзя использовать
  • Каждый расширенный символ переводится в свой charпредставление с использованием std::wctomb
  • Это преобразование имеет смысл только в том случае, если текущая локаль допускает многобайтовое представление символа.
  • Чтобы это работало, приложение должно установить правильную локаль. en_US.utf8кажется достаточно универсальным (имеется на большинстве машин). В Linux доступные локали можно запросить в консоли через locale -aкоманда.

Критика ответа, получившего наибольшее количество голосов

Самый популярный ответ,

      std::wstring ws( args.OptionArg() );
std::string test( ws.begin(), ws.end() );

хорошо работает только тогда, когда широкие символы представляют собой символы ASCII, но это не то, для чего были разработаны широкие символы. В этом решении преобразованная строка содержит один символ на каждый исходный широкий символ, ws.size() == test.size(). Таким образом, он теряет информацию из исходной wstring и создает строки, которые нельзя интерпретировать как правильные последовательности UTF-8. Например, на моей машине строка, полученная в результате этого упрощенного преобразования «ĄŚĆII», печатается как «ZII», хотя ее размер равен 5 (а должен быть 8).

Это старый вопрос, но если это не тот случай, когда вы на самом деле ищете конверсии, а используете TCHAR от Mircosoft для создания ASCII и Unicode, вы можете вспомнить, что std::string действительно

typedef std::basic_string<char> string

Таким образом, мы могли бы определить наш собственный typedef, скажем,

#include <string>
namespace magic {
typedef std::basic_string<TCHAR> string;
}

Тогда вы могли бы использовать magic::string с TCHAR, LPCTSTR, и так далее

Просто для удовольствия:-):

const wchar_t* val = L"hello mfc";
std::string test((LPCTSTR)CString(val));

Следующий код более лаконичен:

wchar_t wstr[500];
char string[500];
sprintf(string,"%ls",wstr);
Другие вопросы по тегам