C ++ Преобразование строки (или char *) в wstring (или wchar_t *)

171
string s = "おはよう";
wstring ws = FUNCTION(s, ws);

Как бы я назначил содержимое s для ws?

Искал в Google и использовал некоторые методы, но они не могут назначить точное содержание. Содержание искажено.

Samir
источник
7
Я не думаю, что stringsпринимает> 8-битные символы. Это уже закодировано в UTF-8?
Kennytm
3
Какая у вас системная кодировка, чтобы она "おはよう"создавала системную строку?
2010 года
Я полагаю, что MSVC примет это и создаст несколько многобайтовых кодировок, возможно, UTF-8.
Potatoswatter
1
@Potatoswatter: MSVC не использует UTF-8 по умолчанию для НИЧЕГО. Если вы вводите эти символы, он спрашивает, в какую кодировку конвертировать файл, и по умолчанию использует кодовую страницу 1252.
Mooing Duck
2
@Samir: что важнее, какова кодировка файла ? Можете ли вы переместить эту строку в начало файла и показать hexdump этой части? Мы можем, вероятно, определить это из этого.
Mooing Duck

Ответы:

239

Предполагая, что входная строка в вашем примере (お は よ う) имеет кодировку UTF-8 (что не выглядит, но давайте предположим, что это ради объяснения :-)) представление строки Unicode Если вы заинтересованы, то ваша проблема может быть полностью решена с помощью стандартной библиотеки (C ++ 11 и новее).

Версия TL; DR:

#include <locale>
#include <codecvt>
#include <string>

std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string narrow = converter.to_bytes(wide_utf16_source_string);
std::wstring wide = converter.from_bytes(narrow_utf8_source_string);

Более длинный онлайн компилируемый и запускаемый пример:

(Все они показывают один и тот же пример. Их просто много для избыточности ...)

Примечание (старое) :

Как указано в комментариях и объяснено в https://stackoverflow.com/a/17106065/6345, существуют случаи, когда использование стандартной библиотеки для преобразования между UTF-8 и UTF-16 может привести к неожиданным различиям в результатах на разных платформах. , Для лучшего преобразования рассмотрите, std::codecvt_utf8как описано на http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Примечание (новое) :

Поскольку codecvtзаголовок устарел в C ++ 17, возникли некоторые опасения по поводу решения, представленного в этом ответе. Однако комитет по стандартизации C ++ добавил важное заявление в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html говоря

этот библиотечный компонент следует перенести в Приложение D вдоль стороны, пока не будет стандартизирована подходящая замена.

Поэтому в обозримом будущем codecvtрешение в этом ответе является безопасным и портативным.

Иоганн Герелл
источник
2
Проверьте, в какой кодировке вы сохраняете файлы VS.
Иоганн Герелл
9
Имейте в виду, что это только C ++ 11!
bk138
1
В minGW (gcc / g ++ 4.8.1 и -std = c ++ 11) заголовок codecvt не существует. Есть ли альтернатива?
Брайан Джек,
1
std::codecvt_utf8
Можете
15
Обратите внимание, что <codecvt>устарела с C ++ 17.
17
47
int StringToWString(std::wstring &ws, const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());

    ws = wsTmp;

    return 0;
}
Пьетро М
источник
93
Это работает, только если все символы являются однобайтовыми, то есть ASCII или ISO-8859-1 . Любой многобайтовый код с треском провалится, включая UTF-8. Вопрос явно содержит многобайтовые символы.
Марк Рэнсом
28
Этот ответ явно недостаточен и делает только копирование узких символов, как есть, в широкие символы. Посмотрите другие ответы, в частности, ответ Иоганна Герелла, чтобы узнать, как правильно перейти от многобайтовой строки или строки в кодировке utf8 к строке wtf16.
DLRdave
10
этот ответ опасен и, вероятно, сломается в системе, отличной от ascii. то есть арабское имя файла будет искажено этим взломом.
Стивен
9
Этот ответ полезен, если вы игнорируете нюанс тела вопроса и сосредотачиваетесь на названии вопроса, что и привело меня сюда из Google. Как, название на вопрос является чрезвычайно вводящей в заблуждение и должны быть изменены , чтобы отразить истинный вопрос Спрашиваемо
Энн Quinn
3
Это работает только для 7-битных символов ASCII. Для latin1 это работает, только если char настроен как unsigned. Если тип char подписан (что чаще всего имеет место), символы> 127 будут давать неправильные результаты.
huyc
32

Ваш вопрос не указан. Строго говоря, этот пример является синтаксической ошибкой. Тем не мение,std::mbstowcs это, вероятно, то, что вы ищете.

Это функция библиотеки C и она работает с буферами, но вот простая в использовании идиома, любезно предоставленная TBohne (ранее Mooing Duck):

std::wstring ws(s.size(), L' '); // Overestimate number of code points.
ws.resize(std::mbstowcs(&ws[0], s.c_str(), s.size())); // Shrink to fit.
Potatoswatter
источник
1
строка s = "お は よ う"; wchar_t * buf = new wchar_t [s.size ()]; size_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); wstring ws (buf, num_chars); // ws = distorted
Самир,
1
@Samir: Вы должны убедиться, что кодировка во время выполнения совпадает с кодировкой во время компиляции. Вам может понадобиться setlocaleили настроить флаги компилятора. Я не знаю, потому что я не использую Windows, но именно поэтому это не обычная функция. Рассмотрим другой ответ, если это возможно.
Potatoswatter
1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
Duck
2
@WaffleSouffle Это устарело. С 2011 года требуются смежные реализации, и реализации выходят из таких уловок задолго до этого.
Potatoswatter
1
а в некоторых средах, таких как mingw, до сих пор нет заголовка codecvt, поэтому некоторые из «лучших» решений ранее не работают, то есть эта проблема все еще не имеет хороших решений в mingw даже по состоянию на декабрь 2014 года
Брайан Джек
18

Только для Windows API, до C ++ 11, если кому-то это нужно:

#include <stdexcept>
#include <vector>
#include <windows.h>

using std::runtime_error;
using std::string;
using std::vector;
using std::wstring;

wstring utf8toUtf16(const string & str)
{
   if (str.empty())
      return wstring();

   size_t charsNeeded = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), NULL, 0);
   if (charsNeeded == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   vector<wchar_t> buffer(charsNeeded);
   int charsConverted = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), &buffer[0], buffer.size());
   if (charsConverted == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   return wstring(&buffer[0], charsConverted);
}
Алекс Че
источник
Вы можете оптимизировать это. Нет необходимости делать двойную копию строки, используя vector. Просто Оставляем символы в строке, делая wstring strW(charsNeeded + 1);и затем использовать его в качестве буфера для преобразования: &strW[0]. Наконец, убедитесь, что последний нулевой присутствует после преобразования, выполнивstrW[charsNeeded] = 0;
c00000fd
1
@ c00000fd, насколько я знаю, внутренний буфер std :: basic_string должен быть непрерывным только начиная со стандарта C ++ 11. Мой код до C ++ 11, как отмечено в верхней части поста. Поэтому код & strW [0] не будет соответствовать стандарту и может законно завершиться сбоем во время выполнения.
Алекс Че
13

Если вы используете Windows / Visual Studio и вам нужно преобразовать строку в wstring, вы можете использовать:

#include <AtlBase.h>
#include <atlconv.h>
...
string s = "some string";
CA2W ca2w(s.c_str());
wstring w = ca2w;
printf("%s = %ls", s.c_str(), w.c_str());

Та же процедура для преобразования wstring в строку (иногда вам нужно будет указать кодовую страницу ):

#include <AtlBase.h>
#include <atlconv.h>
...
wstring w = L"some wstring";
CW2A cw2a(w.c_str());
string s = cw2a;
printf("%s = %ls", s.c_str(), w.c_str());

Вы можете указать кодовую страницу и даже UTF8 (это очень хорошо при работе с JNI / Java ). В этом ответе показан стандартный способ преобразования std :: wstring в utf8 std :: string .

// 
// using ATL
CA2W ca2w(str, CP_UTF8);

// 
// or the standard way taken from the answer above
#include <codecvt>
#include <string>

// convert UTF-8 string to wstring
std::wstring utf8_to_wstring (const std::string& str) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.from_bytes(str);
}

// convert wstring to UTF-8 string
std::string wstring_to_utf8 (const std::wstring& str) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.to_bytes(str);
}

Если вы хотите узнать больше о кодовых страницах, то есть интересная статья о Джоэле о программном обеспечении: Абсолютный минимум, который должен знать каждый разработчик программного обеспечения. .

Эти макросы CA2W (Convert Ansi to Wide = unicode) являются частью макросов преобразования строк ATL и MFC , включая примеры.

Иногда вам нужно отключить предупреждение безопасности # 4995 ', я не знаю другого обходного пути (для меня это случилось, когда я скомпилировал для WindowsXp в VS2012).

#pragma warning(push)
#pragma warning(disable: 4995)
#include <AtlBase.h>
#include <atlconv.h>
#pragma warning(pop)

Редактировать: Ну, в соответствии с этой статьей, статья Джоэля выглядит так: «Хотя это забавно, она довольно легка в реальных технических деталях». Статья: Что каждый программист абсолютно, положительно должен знать о кодировании и наборах символов для работы с текстом .

lmiguelmh
источник
Извините, я не являюсь носителем английского языка. Пожалуйста, отредактируйте, как считаете нужным.
lmiguelmh
Что случилось с Downvoter? Что не так с ответом?
lmiguelmh
Вероятно, факт, что это продвигает непереносимый код.
Павел Минаев
Да, именно поэтому я заявил, что это работает только в Windows / Visual Studio. Но, по крайней мере, это решение верное, а не это:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh
Дополнительное примечание: CA2W находится под пространством имен ATL. (ATL :: CA2W)
Val
12

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

Это НЕ работает для многобайтовых кодировок символов. Это просто глупый способ отбросить безопасность типов и расширить 7-битные символы из std :: string в младшие 7 бит каждого символа std: wstring. Это полезно только в том случае, если у вас есть 7-битные строки ASCII и вам нужно вызвать API, который требует широких строк.

#include <sstream>

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

std::wstringstream cls;
cls << " abc " << narrow.c_str() << L" def " << wide.c_str();
std::wstring total= cls.str();
Марк Лаката
источник
Ответ кажется интересным. Не могли бы вы объяснить немного: будет ли это работать для многобайтовых кодировок и почему / как?
wh1t3cat1k
схемы кодирования ортогональны классу хранения. stringхранит 1-байтовые символы и wstringсохраняет 2-байтовые символы. что-то вроде utf8 хранит многобайтовые символы как последовательность значений из 1 байта, то есть в a string. классы строки не помогают с кодированием. Я не эксперт по кодированию классов в с ++.
Марк Лаката
2
Любая причина, почему этот не самый лучший ответ, учитывая, насколько он короток и прост? Какие-либо случаи, которые это не покрывает?
Рю,
@MarkLakata, я прочитал твой ответ на первый комментарий, но все еще не уверен. Будет ли это работать для многобайтовых символов? Другими словами, разве это не подвержено той же ловушке, что и этот ответ ?
23
@ Marc.2377 Это НЕ работает для многобайтовых кодировок символов. Это просто глупый способ отбросить безопасность типов и расширить 7-битные символы из std::stringнижних 7-бит каждого символа std:wstring. Это полезно только в том случае, если у вас есть 7-битные строки ASCII и вам нужно вызвать API, который требует широких строк. Посмотрите на stackoverflow.com/a/8969776/3258851, если вам нужно что-то более сложное.
Марк Лаката
11

С char*по wstring:

char* str = "hello worlddd";
wstring wstr (str, str+strlen(str));

С stringпо wstring:

string str = "hello worlddd";
wstring wstr (str.begin(), str.end());

Обратите внимание, что это работает только в том случае, если преобразуемая строка содержит только символы ASCII.

Ghominejad
источник
7
Потому что это работает, только если кодировка Windows-1252, которая не может даже содержать буквы в вопросе.
Mooing Duck
3
это наименее подверженный ошибкам способ сделать это, когда вы знаете, что имеете дело с ASCII. Что является важным примером использования при портировании приложений на новые API.
Сид Сарасвати
Это не так. Если вы используете Visual Studio, вы должны использовать atlconv.h. Проверьте другие ответы.
lmiguelmh
7

используя Boost.Locale:

ws = boost::locale::conv::utf_to_utf<wchar_t>(s);
vladon
источник
5

Этот вариант мой любимый в реальной жизни. Он преобразует вход, если это действительно UTF-8, в соответствующий wstring. Если входной сигнал поврежден, он wstringсостоит из отдельных байтов. Это очень полезно, если вы не можете быть уверены в качестве входных данных.

std::wstring convert(const std::string& input)
{
    try
    {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        return converter.from_bytes(input);
    }
    catch(std::range_error& e)
    {
        size_t length = input.length();
        std::wstring result;
        result.reserve(length);
        for(size_t i = 0; i < length; i++)
        {
            result.push_back(input[i] & 0xFF);
        }
        return result;
    }
}
Матиас Ронге
источник
1
Я только что запустил этот вопрос на основе вашего ответа stackoverflow.com/questions/49669048/… любезно посмотрите
MistyD
2

Если у вас есть QT и вам лень реализовывать функцию и прочее, вы можете использовать

std :: string str; QString (ул) .toStdWString ()

Кадир Эрдем Демир
источник
Почти, но вы должны просто начать с a QString, потому что QStringконструктор по какой-то причине не может принять строку.
бобсбенджамин
1
Вы можете использовать doc.qt.io/qt-5/qstring.html#fromStdString
Кадир Эрдем Демир
Это хорошо. Также вы можете использовать .c_str (), чтобы позволить QString принять вашу строку в конструкторе.
Miep
1

Метод s2ws работает хорошо. Надежда помогает.

std::wstring s2ws(const std::string& s) {
    std::string curLocale = setlocale(LC_ALL, ""); 
    const char* _Source = s.c_str();
    size_t _Dsize = mbstowcs(NULL, _Source, 0) + 1;
    wchar_t *_Dest = new wchar_t[_Dsize];
    wmemset(_Dest, 0, _Dsize);
    mbstowcs(_Dest,_Source,_Dsize);
    std::wstring result = _Dest;
    delete []_Dest;
    setlocale(LC_ALL, curLocale.c_str());
    return result;
}
hahakubile
источник
6
Что со всеми этими ответами выделяет динамическую память небезопасным способом, а затем копирует данные из буфера в строку? Почему никто не избавляется от небезопасного посредника?
Mooing Duck
хахакубиле, можете ли вы помочь с чем-то похожим для ws2s?
Кристиан
1

Основываясь на моем собственном тестировании (на Windows 8, vs2010), mbstowcs может фактически повредить исходную строку, она работает только с кодовой страницей ANSI. Если MultiByteToWideChar / WideCharToMultiByte также может привести к повреждению строки - но они имеют тенденцию заменять символы, которые они не знают, на «?» вопросительные знаки, но mbstowcs имеет тенденцию останавливаться, когда встречает неизвестный символ и обрезает строку в этой самой точке. (Я проверил вьетнамские символы на финских окнах).

Так что предпочтите Multi * -Windows API-функции по сравнению с аналоговыми и C-функциями.

Кроме того, я заметил, что самый короткий способ кодирования строки из одной кодовой страницы в другую - это не вызовы функций API MultiByteToWideChar / WideCharToMultiByte, а их аналоговые макросы ATL: W2A / A2W.

Таким образом, аналоговая функция, как упомянуто выше, звучит так:

wstring utf8toUtf16(const string & str)
{
   USES_CONVERSION;
   _acp = CP_UTF8;
   return A2W( str.c_str() );
}

_acp объявлен в макросе USES_CONVERSION.

Или также функцию, которую я часто пропускаю при выполнении преобразования старых данных в новые:

string ansi2utf8( const string& s )
{
   USES_CONVERSION;
   _acp = CP_ACP;
   wchar_t* pw = A2W( s.c_str() );

   _acp = CP_UTF8;
   return W2A( pw );
}

Но обратите внимание, что эти макросы используют большой стек - не используйте для циклов или рекурсивных циклов для одной и той же функции - после использования макроса W2A или A2W - лучше вернуть ASAP, поэтому стек будет освобожден от временного преобразования.

TarmoPikaro
источник
1

Строка в строку

std::wstring Str2Wstr(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

wstring to String

std::string Wstr2Str(const std::wstring& wstr)
{
    typedef std::codecvt_utf8<wchar_t> convert_typeX;
    std::wstring_convert<convert_typeX, wchar_t> converterX;
    return converterX.to_bytes(wstr);
}
Исма Рекатакусума
источник
1
У этого Str2Wstr есть проблема с завершением 0. Больше невозможно объединить сгенерированные строки w через «+» (как в wstring s3 = s1 + s2). Я скоро выложу ответ, решающий эту проблему. Сначала нужно провести тестирование на утечки памяти.
белый бамбук
-2

string s = "おはよう"; это ошибка.

Вы должны использовать wstring напрямую:

wstring ws = L"おはよう";
Томас Бонини
источник
1
Это тоже не сработает. Вам придется конвертировать эти не-BMP символы в C escape-последовательности.
Дейв Ван ден Эйнде
3
@Dave: он работает, если ваш компилятор поддерживает юникод в исходных файлах, и все те, что были в последнее десятилетие (Visual Studio, GCC, ...)
Томас Бонини
Привет, независимо от используемой по умолчанию системной кодировки (например, я могу использовать арабский в качестве системной кодировки по умолчанию), как должна работать кодировка файла исходного кода для L "お は よ う"? это должно быть в UTF-16, или я могу иметь UTF-8 без спецификации для кодировки файла .cpp?
Afriza N. Arief
2
@afriza: это не имеет значения, если ваш компилятор поддерживает это
Томас Бонини,
2
Это не ошибка; расширенные символы в «узкой» строке определяются для отображения на многобайтовые последовательности. Компилятор должен поддерживать его до тех пор, пока это делает ОС, что является наименьшим спросом.
Potatoswatter
-2

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

std::wstring string2wString(const std::string& s){
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

int main(){
    std::wstring str="your string";
    std::wstring wStr=string2wString(str);
    return 0;
}
ягуар
источник
3
Обратите внимание, что вопрос не упоминает о Windows, и этот ответ только для Windows.
Иоганн Герелл
CP_ACPэто, безусловно, неправильный аргумент. Внезапно состояние среды исполняющего потока влияет на поведение кода. Не рекомендуется Укажите фиксированную кодировку символов в конверсии. (И рассмотрим обработку ошибок.)
IInspectable