C ++ Перетворити рядок (або char *) у wstring (або wchar_t *)


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

Як я можу призначити вміст s до ws?

Шукали Google і використовували деякі методи, але вони не можуть призначити точний вміст. Зміст спотворено.


7
Я не думаю, що stringsприймає> 8-бітові символи. Це вже закодовано в UTF-8?
— kennytm

3
Яке кодування у вашій системі зробило б "おはよう"кодовану системою рядок?
— sbi

Я вірю, що MSVC прийме це і зробить це деяким багатобайтовим кодуванням, можливо, UTF-8.
— Potatoswatter

1
@Potatoswatter: MSVC не використовує UTF-8 за замовчуванням для будь-якого. Якщо ви вводите ці символи, він запитує, в яке кодування перетворити файл, а за замовчуванням - на кодову сторінку
— 1252.

2
@Samir: важливіше, що таке кодування файлу ? Чи можете ви перемістити цей рядок на початок файлу і показати шестнадцяткову частину? Ми можемо з цього ідентифікувати.
— 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для початківців
— Noitidart

14
Зверніть увагу, що <codecvt>ця система застаріла після C ++ 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 кодованої рядки до utf16 wstring.
— DLRdave

10
ця відповідь небезпечна і, ймовірно, порушиться в системі, що не має права. тобто арабське ім'я файлу буде зламане цим хаком.
— Стівен

9
Ця відповідь корисна, якщо ви ігноруєте нюанси тіла питання та зосереджуєтесь на заголовку питання, що саме мене привело сюди від Google. Так, заголовок питання є надзвичайно оманливим, і його слід змінити, щоб відобразити справжнє запитання
— Енн Квінн

3
Це працює лише для 7-бітових символів ASCII. Для latin1 він працює лише в тому випадку, якщо char налаштовано як непідписаний. Якщо знак char підписано (що в більшості випадків трапляється), символи> 127 дадуть неправильні результати.
— huyc

32

Ваше запитання не визначено. Суворо, цей приклад - синтаксична помилка. Однак, std::mbstowcsмабуть, те, що ви шукаєте.

Це функція бібліотеки С і працює над буферами, але ось легка у користуванні ідіома, люб’язність 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.

1
рядок s = "お は よ う"; wchar_t * buf = новий wchar_t [s.size ()]; size_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); wstring ws (buf, num_chars); // ws = спотворено
— Самір

1
@Samir: Ви повинні переконатися, що кодування часу виконання збігається з кодуванням часу компіляції. Можливо, вам знадобиться setlocaleабо налаштувати прапори компілятора. Я не знаю, тому що не використовую Windows, але це не звичайна особливість. Розгляньте іншу відповідь, якщо це можливо.
— Potatoswatter

1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
— Mooing Duck

2
@WaffleSouffle Це застаріло. Постійні впровадження потрібні з 2011 року, і впровадження таких проблем виходило задовго до цього.
— Potatoswatter

1
а в деяких середовищах, таких як mingw, все ще немає заголовка codecvt, тому деякі "кращі" рішення раніше не працюють, тобто ця проблема все ще не має хороших рішень у mingw навіть станом на грудень 2014 року
— Брайан Джек

18

Тільки API 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);
}

Ви можете оптимізувати його. Не потрібно робити подвійну копію рядка, використовуючи a 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());

Та сама процедура перетворення вставки в рядок (іноді вам потрібно буде вказати сторінку коду ):

#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 ). Стандартний спосіб перетворення ЗОГО :: wstring в utf8 StD :: рядок показав в цій відповіді .

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

Якщо ви хочете дізнатися більше про кодові сторінки , є цікава стаття про Joel про програмне забезпечення: Абсолютний мінімум кожного розробника програмного забезпечення абсолютно, позитивно повинен знати про набори юнікоду та символів .

Ці макроси CA2W (Перетворити Ansi в широкий = unicode) є частиною макросів ATL і MFC String Conversion , включені зразки.

Іноді вам потрібно буде відключити попередження про безпеку № 4995 ', я не знаю інших способів (мені це трапляється, коли я компілював для WindowsXp у VS2012).

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

Редагувати: Що ж, згідно з цією статтею, як видається, стаття Джоеля: "хоча і розважає, вона досить сильно висвітлює фактичні технічні деталі". Стаття: Що кожен програміст абсолютно, позитивно повинен знати про кодування та набір символів для роботи з текстом .


Вибачте, я не є носієм англійської мови. Відредагуйте, як вважаєте за потрібне.
— lmiguelmh

Що сталося з потоком? Що не так у відповіді?
— 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. класи рядків не допомагають при кодуванні. Я не фахівець з класів кодування в c ++.
— Марк Лаката

2
Будь-яка причина, чому це не найкраща відповідь, враховуючи, наскільки це коротко і просто? Будь-які справи, які вона не охоплює?
— Рюу

@MarkLakata, я прочитав вашу відповідь на перший коментар, але все ще не впевнений. Чи буде це працювати для багатобайтових символів? Іншими словами, хіба він не схильний до тієї ж грі, як ця відповідь ?
— Марк.2377,

@ Marc.2377 Це НЕ працює для багатобайтових кодувань символів. Це лише тупий спосіб викинути безпеку типу та розширити 7-бітові символи з std::string7 нижчих біт кожного символу 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.


7
Тому що це працює лише в тому випадку, якщо кодуванням є Windows-1252, який навіть не може містити літери у питанні.
— Mooing Duck

3
це найменше схильний до помилок спосіб, коли ви знаєте, що маєте справу з ASCII. Що є видатним шаблоном при переносі програм на новіші api.
— Сид Сарасвати

Це не шлях. Якщо ви використовуєте Visual Studio, вам слід скористатися atlconv.h. Перевірте інші відповіді.
— lmiguelmh


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 (str) .toStdWString ()


Майже, але вам слід просто почати з символу a QString, оскільки QStringконструктор чомусь не може прийняти рядок.
— bobsbeenjamin

1
Ви можете використовувати doc.qt.io/qt-5/qstring.html#fromStdString
— Kadir Erdem Demir

Це добре. Також ви можете використовувати .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;
}

6
Що з усіма цими відповідями розподіляти динамічну пам'ять небезпечно, а потім копіювати дані з буфера в рядок? Чому ніхто не позбавляється від небезпечного посередника?
— Mooing Duck

hahakubile, ви можете допомогти, будь ласка, чимось подібним для ws2s?
— Крістіан

1

На основі мого власного тестування (у Windows 8, vs2010) mbstowcs може фактично пошкодити оригінальний рядок, він працює лише з кодовою сторінкою ANSI. Якщо MultiByteToWideChar / WideCharToMultiByte також може спричинити пошкодження рядків - але вони, як правило, замінюють символи, які вони не знають на "?" знаки запитання, але mbstowcs має тенденцію зупинятися, коли він стикається з невідомим символом і вирізає рядок саме в цій точці. (Я випробував в'єтнамські символи на фінських вікнах).

Тому віддайте перевагу функції Multi *-windows api над аналоговими функціями ansi C.

Крім того, що я помітив найкоротший спосіб кодування рядка з однієї кодової сторінки на іншу, це не використання MultiByteToWideChar / WideCharToMultiByte api функцій викликів, а їх аналогічні макроси 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 - краще повернути якнайшвидше, тому стек буде звільнений від тимчасової конверсії.


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

прив’язання до рядка

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 припиненням. Зв'язати згенеровані wstrings більше неможливо через "+" (як у wstring s3 = s1 + s2). Я опублікую відповідь, скоро вирішивши цю проблему. Спершу слід провести тестування на витоки пам'яті.
— thewhiteambit

-2

string s = "おはよう"; є помилкою.

Ви повинні використовувати wstring безпосередньо:

wstring ws = L"おはよう";

1
Це теж не вийде. Вам доведеться конвертувати ці символи, що не є BMP, у послідовності втечі C.
— Дейв Ван ден Ейнде

3
@Dave: це спрацьовує, якщо ваш компілятор підтримує unicode у вихідних файлах, а всі ті, що знаходяться за останнє десятиліття (візуальна студія, gcc, ...)
— Thomas Bonini

Привіт, незалежно від кодування системи за замовчуванням (у мене може бути арабська мова як кодування системи за замовчуванням, наприклад), що має кодувати файл вихідного коду для L "お は よ う"? це повинно бути в UTF-16, чи можна мати UTF-8 без BOM для кодування файлу .cpp?
— Афріза Н. Короткий

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це, звичайно, неправильний аргумент. Раптом стан оточуючого середовища виконавчої нитки впливає на поведінку коду. Не рекомендується. Вкажіть фіксовану кодування символів у конверсії. (І розглядайте помилки обробки.)
— Неочікувана
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.