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.