C ++ Converti stringa (o char *) in wstring (o wchar_t *)


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

Come assegnerei il contenuto di s a ws?

Hanno cercato su Google e utilizzato alcune tecniche ma non possono assegnare il contenuto esatto. Il contenuto è distorto.


7
Non credo che stringsaccetta caratteri a 8 bit. È già codificato in UTF-8?
kennytm,

3
Qual è la tua codifica di sistema per creare "おはよう"una stringa con codifica di sistema?
sbi,

Credo che MSVC lo accetti e ne faccia una codifica multibyte, forse UTF-8.
Potatoswatter

1
@Potatoswatter: MSVC non utilizza UTF-8 per impostazione predefinita per ANYTHING. Se si immettono quei caratteri, viene richiesta la codifica in cui convertire il file e viene impostata automaticamente la tabella codici 1252.
Mooing Duck

2
@Samir: più importante è la codifica del file ? Puoi spostare quella stringa all'inizio del file e mostrare un dump esadecimale di quella parte? Probabilmente possiamo identificarlo da quello.
Mooing Duck,

Risposte:


239

Supponendo che la stringa di input nel tuo esempio (お は よ う) sia una codifica UTF-8 (che non è, a quanto pare, ma supponiamo che sia per il bene di questa spiegazione :-)) rappresentazione di una stringa Unicode di tuo interesse, quindi il tuo problema può essere risolto completamente con la sola libreria standard (C ++ 11 e successive).

La versione 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);

Esempio compilabile e eseguibile online più lungo:

(Tutti mostrano lo stesso esempio. Ce ne sono solo molti per la ridondanza ...)

Nota (vecchia) :

Come sottolineato nei commenti e spiegato in https://stackoverflow.com/a/17106065/6345 ci sono casi in cui l'uso della libreria standard per la conversione tra UTF-8 e UTF-16 potrebbe dare differenze inaspettate nei risultati su piattaforme diverse . Per una migliore conversione, considerare std::codecvt_utf8come descritto su http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Nota (nuova) :

Poiché l' codecvtintestazione è obsoleta in C ++ 17, sono state sollevate alcune preoccupazioni in merito alla soluzione presentata in questa risposta. Tuttavia, il comitato per gli standard C ++ ha aggiunto una dichiarazione importante in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html dicendo

questo componente della biblioteca dovrebbe essere ritirato all'allegato D, a fianco, fino a quando non viene standardizzata una sostituzione adeguata.

Quindi, nel prossimo futuro, la codecvtsoluzione in questa risposta è sicura e portatile.


2
Controlla con quale codifica salvi i file VS
Johann Gerell

9
Ricorda che questo è solo C ++ 11!
bk138,

1
In minGW (gcc / g ++ 4.8.1 e -std = c ++ 11) l'intestazione codecvt non esiste. C'è un'alternativa?
Brian Jack,

1
std::codecvt_utf8
Potresti

15
Si noti che <codecvt>è obsoleto dal C ++ 17.
tambre,

47
int StringToWString(std::wstring &ws, const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());

    ws = wsTmp;

    return 0;
}

93
Funziona solo se tutti i caratteri sono a byte singolo, ovvero ASCII o ISO-8859-1 . Qualsiasi cosa a più byte fallirà miseramente, incluso UTF-8. La domanda contiene chiaramente caratteri multibyte.
Mark Ransom,

28
Questa risposta è chiaramente insufficiente e non fa altro che copiare caratteri stretti come in caratteri ampi. Vedi le altre risposte, in particolare quella di Johann Gerell, su come passare correttamente da una stringa codificata multi-byte o utf8 a una stringa utf16.
DLRdave,

10
questa risposta è pericolosa e probabilmente si interromperà su un sistema non ASCII. cioè un nome di file arabo sarà rovinato da questo hack.
Stephen,

9
Questa risposta è utile se ignori la sfumatura del corpo della domanda e ti concentri sul titolo della domanda, che è ciò che mi ha portato qui da Google. Così com'è, il titolo della domanda è estremamente fuorviante e dovrebbe essere modificato per riflettere la vera domanda posta
Anne Quinn,

3
Funziona solo con caratteri ASCII a 7 bit. Per latin1, funziona solo se char è configurato come unsigned. Se il carattere char è firmato (che è il più delle volte il caso), i caratteri> 127 daranno risultati errati.
huyc,

32

La tua domanda non è specificata In senso stretto, questo esempio è un errore di sintassi. Tuttavia, std::mbstowcsè probabilmente quello che stai cercando.

È una funzione C-library e opera su buffer, ma ecco un linguaggio facile da usare, per gentile concessione di TBohne (precedentemente 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
string 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 = distorto
Samir

1
@Samir: devi assicurarti che la codifica di runtime sia la stessa della codifica in fase di compilazione. Potrebbe essere necessario setlocaleo modificare i flag del compilatore. Non lo so perché non utilizzo Windows, ma è per questo che non è una funzionalità comune. Considera l'altra risposta, se possibile.
Potatoswatter

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

2
@WaffleSouffle Non è aggiornato. Dal 2011 sono richieste implementazioni contigue e le implementazioni hanno abbandonato tali trucchi molto prima.
Potatoswatter,

1
e alcuni ambienti come mingw non hanno ancora l'intestazione codecvt, quindi alcune delle soluzioni "migliori" precedenti non funzionano, il che significa che questo problema non ha ancora buone soluzioni a mingw anche a partire da dicembre 2014
Brian Jack,

18

Solo API Windows, implementazione pre C ++ 11, nel caso qualcuno ne abbia bisogno:

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

Puoi ottimizzarlo. Non è necessario eseguire una doppia copia della stringa utilizzando a vector. Basta prenotare i caratteri della stringa da fare wstring strW(charsNeeded + 1);e quindi utilizzarlo come buffer per la conversione: &strW[0]. Infine assicurati che sia presente l'ultimo null dopo la conversione facendostrW[charsNeeded] = 0;
c00000fd

1
@ c00000fd, per quanto ne so, il buffer interno std :: basic_string deve essere continuo solo dallo standard C ++ 11. Il mio codice è pre-C ++ 11, come indicato nella parte superiore del post. Pertanto, il codice & strW [0] non sarebbe conforme allo standard e potrebbe arrestarsi in modo legittimo in fase di esecuzione.
Alex Che

13

Se stai usando Windows / Visual Studio e devi convertire una stringa in wstring puoi usare:

#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());

Stessa procedura per convertire una stringa in stringa (a volte è necessario specificare una tabella codici ):

#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());

È possibile specificare una tabella codici e persino UTF8 (che è piuttosto carino quando si lavora con JNI / Java ). In questa risposta viene mostrato un modo standard per convertire una stringa std :: wstring in utf8 std :: stringa .

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

Se vuoi saperne di più sui codici, c'è un interessante articolo su Joel on Software: il minimo assoluto che ogni sviluppatore di software deve assolutamente conoscere positivamente su Unicode e set di caratteri .

Queste macro CA2W (Converti Ansi in Wide = unicode) fanno parte delle macro di conversione stringhe ATL e MFC , inclusi gli esempi.

A volte dovrai disabilitare l'avviso di sicurezza # 4995 ', non conosco altre soluzioni alternative (a me succede quando ho compilato per WindowsXp in VS2012).

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

Modifica: Beh, secondo questo articolo l'articolo di Joel sembra essere: "mentre è divertente, è abbastanza leggero sui dettagli tecnici reali". Articolo: Ciò che ogni programmatore deve assolutamente e positivamente sapere sulla codifica e sui set di caratteri per lavorare con il testo .


Mi dispiace non sono un madrelingua inglese. Modifica come ritieni opportuno.
lmiguelmh,

Cosa succede con il downvoter? Cosa c'è di sbagliato nella risposta?
lmiguelmh,

Probabilmente il fatto che promuove il codice non portatile.
Pavel Minaev,

Sì, ecco perché ho affermato che funziona solo in Windows / Visual Studio. Ma almeno questa soluzione è corretta, e non questa:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh,

Nota aggiuntiva: CA2W si trova nello spazio dei nomi di ATL. (ATL :: CA2W)
Val

12

Ecco un modo di combinare string, wstringe costanti stringa misti a wstring. Usa la wstringstreamclasse.

Questo NON funziona per le codifiche di caratteri multi-byte. Questo è solo un modo stupido di gettare via la sicurezza del tipo ed espandere i caratteri a 7 bit da std :: string nei 7 bit inferiori di ogni carattere di std: wstring. Ciò è utile solo se si dispone di stringhe ASCII a 7 bit e è necessario chiamare un'API che richiede stringhe estese.

#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();

La risposta sembra interessante. Potresti spiegare un po ': funzionerà per le codifiche multi-byte e perché / come?
wh1t3cat1k,

gli schemi di codifica sono ortogonali alla classe di archiviazione. stringmemorizza i caratteri da 1 byte e wstringmemorizza i caratteri da 2 byte. qualcosa come utf8 memorizza i caratteri mulitbyte come una serie di valori di 1 byte, cioè in a string. le classi di stringhe non aiutano con la codifica. Non sono un esperto di codifica delle classi in c ++.
Mark Lakata,

2
Qualche motivo per cui questa non è la risposta migliore, dato quanto è breve e semplice? Qualche caso che non copre?
Ryuu,

@MarkLakata, ho letto la tua risposta al primo commento ma non sono ancora sicuro. Funzionerà con caratteri multibyte? In altre parole, non è incline alla stessa trappola di questa risposta ?
Marc.2377,

@ Marc.2377 Questo NON funziona per le codifiche di caratteri multi-byte. Questo è solo un modo stupido di gettare via la sicurezza del tipo ed espandere i caratteri std::stringa 7 bit nei 7 bit inferiori di ogni carattere di std:wstring. Ciò è utile solo se si dispone di stringhe ASCII a 7 bit e è necessario chiamare un'API che richiede stringhe estese. Guarda stackoverflow.com/a/8969776/3258851 se hai bisogno di qualcosa di più sofisticato.
Mark Lakata,

11

Da char*a wstring:

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

Da stringa wstring:

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

Nota che funziona bene solo se la stringa da convertire contiene solo caratteri ASCII.


7
Perché questo funziona solo se la codifica è Windows-1252, che non può nemmeno contenere le lettere nella domanda.
Mooing Duck

3
questo è il modo meno propenso per farlo, quando sai che hai a che fare con ASCII. Che è un caso d'uso importante quando si esegue il porting di app su API più recenti.
Sid Sarasvati,

Questo è non è il modo. Se si utilizza Visual Studio, è necessario utilizzare atlconv.h. Controlla le altre risposte.
lmiguelmh,

7

utilizzando Boost.Locale:

ws = boost::locale::conv::utf_to_utf<wchar_t>(s);

5

Questa variante è la mia preferita nella vita reale. Converte l'ingresso, se è UTF-8 valido , nel rispettivo wstring. Se l'input è danneggiato, wstringviene costruito dai singoli byte. Questo è estremamente utile se non puoi davvero essere sicuro della qualità dei tuoi dati di input.

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
Ho appena lanciato questa domanda in base alla tua risposta stackoverflow.com/questions/49669048/… puoi gentilmente dare un'occhiata
MistyD

2

Se hai QT e se sei pigro per implementare una funzione e cose che puoi usare

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


Quasi, ma dovresti semplicemente iniziare con a QString, perché il QStringcostruttore non può accettare una stringa per qualche motivo.
bobsbeenjamin,


Questo è carino. Inoltre, puoi usare .c_str () per consentire a QString di accettare la tua stringa nel costruttore.
miep

1

il metodo s2ws funziona bene. La speranza aiuta.

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
Che cosa succede con tutte queste risposte allocare la memoria dinamica in modo non sicuro e quindi copiare i dati dal buffer alla stringa? Perché nessuno si libera del mediatore insicuro?
Mooing Duck

hahakubile, puoi aiutare per favore con qualcosa di simile per ws2s?
cristian,

1

Basato sul mio test (su Windows 8, vs2010) mbstowcs può effettivamente danneggiare la stringa originale, funziona solo con la tabella codici ANSI. Se MultiByteToWideChar / WideCharToMultiByte può anche causare il danneggiamento delle stringhe, ma tendono a sostituire i caratteri che non conoscono con "?" punti interrogativi, ma mbstowcs tende a fermarsi quando incontra un carattere sconosciuto e taglia la stringa proprio in quel punto. (Ho testato personaggi vietnamiti su finestre finlandesi).

Quindi preferisci la funzione API * Multi-windows rispetto alle funzioni analogiche C.

Inoltre, ciò che ho notato il modo più breve per codificare la stringa da una tabella codici a un'altra non è usare le chiamate della funzione API MultiByteToWideChar / WideCharToMultiByte ma i loro macro ATL analogici: W2A / A2W.

Quindi la funzione analogica sopra menzionata suona come:

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

_acp è dichiarato nella macro USES_CONVERSION.

O anche la funzione che spesso mi manca quando eseguo la conversione di dati vecchi in uno nuovo:

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

   _acp = CP_UTF8;
   return W2A( pw );
}

Ma tieni presente che quelle macro usano pesantemente lo stack - non utilizzare per loop o loop ricorsivi per la stessa funzione - dopo aver usato macro W2A o A2W - meglio per restituire al più presto, quindi lo stack verrà liberato dalla conversione temporanea.


1

Stringa a filo

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 a 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
Questo Str2Wstr ha un problema con terminazione 0. Non è più possibile concatenare i wstring generati tramite "+" (come in wstring s3 = s1 + s2). Invierò presto una risposta per risolvere questo problema. Prima devi fare alcuni test per le perdite di memoria.
thewhiteambit

-2

string s = "おはよう"; è un errore.

Dovresti usare direttamente wstring:

wstring ws = L"おはよう";

1
Neanche questo funzionerà. Dovrai convertire quei caratteri non BMP in sequenze di escape C.
Dave Van den Eynde,

3
@Dave: funziona se il tuo compilatore supporta unicode nei file sorgente, e tutti quelli dell'ultimo decennio lo fanno (visual studio, gcc, ...)
Thomas Bonini

Salve, indipendentemente dalla codifica di sistema predefinita (ad esempio, potrei avere l'arabo come codifica di sistema predefinita), come dovrebbe funzionare la codifica del file di codice sorgente per L "お は よ う"? dovrebbe essere in UTF-16 o posso avere UTF-8 senza BOM per la codifica del file .cpp?
Afriza N. Arief,

2
@afriza: non importa se la tua compilazione lo supporta
Thomas Bonini,

2
Non è un errore; i caratteri estesi in una stringa "stretta" sono definiti per mappare a sequenze multibyte. Il compilatore dovrebbe supportarlo fino a quando lo fa il sistema operativo, che è il minimo che puoi chiedere.
Potatoswatter,

-2

usa questo codice per convertire la stringa in 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
Nota che la domanda non menziona Windows e questa risposta è solo Windows.
Johann Gerell,

CP_ACPè sicuramente l'argomento sbagliato. Improvvisamente, lo stato dell'ambiente del thread in esecuzione ha un effetto sul comportamento del codice. Non consigliabile. Specifica una codifica a caratteri fissi nella conversione. (E considerare la gestione degli errori.)
Indispensabile il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.