C ++ Convertit une chaîne (ou char *) en wstring (ou wchar_t *)


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

Comment attribuer le contenu de s à ws?

Recherche Google et utilisation de certaines techniques, mais ils ne peuvent pas attribuer le contenu exact. Le contenu est déformé.


7
Je ne pense pas qu'il stringsaccepte les caractères> 8 bits. Est-il déjà encodé en UTF-8?
kennytm

3
Quel est le codage de votre système pour créer "おはよう"une chaîne codée par le système?
sbi

Je crois que MSVC acceptera cela et en fera un encodage multi-octets, peut-être UTF-8.
Potatoswatter le

1
@Potatoswatter: MSVC n'utilise pas UTF-8 par défaut pour TOUT. Si vous entrez ces caractères, il vous demande dans quel encodage convertir le fichier et la page de code par défaut est 1252.
Mooing Duck

2
@Samir: le plus important est quel est l'encodage du fichier ? Pouvez-vous déplacer cette chaîne au début du fichier et afficher un hexdump de cette partie? Nous pouvons probablement l'identifier à partir de là.
Mooing Duck

Réponses:


239

En supposant que la chaîne d'entrée dans votre exemple (お は よ う) est une représentation encodée en UTF-8 (ce qui n'est pas le cas, à première vue, mais supposons que ce soit pour cette explication :-)) représentation d'une chaîne Unicode de votre intérêt, alors votre problème peut être entièrement résolu avec la bibliothèque standard (C ++ 11 et plus récent) seule.

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

Exemple plus long compilable et exécutable en ligne:

(Ils montrent tous le même exemple. Il y en a juste beaucoup pour la redondance ...)

Remarque (ancienne) :

Comme indiqué dans les commentaires et expliqué dans https://stackoverflow.com/a/17106065/6345, il existe des cas où l'utilisation de la bibliothèque standard pour convertir entre UTF-8 et UTF-16 peut donner des différences inattendues dans les résultats sur différentes plates-formes . Pour une meilleure conversion, considérez std::codecvt_utf8comme décrit sur http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Remarque (nouveau) :

Étant donné que l'en- codecvttête est obsolète en C ++ 17, certaines inquiétudes concernant la solution présentée dans cette réponse ont été soulevées. Cependant, le comité des normes C ++ a ajouté une déclaration importante dans http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html disant

ce composant de bibliothèque devrait être retiré à l'annexe D, parallèlement, jusqu'à ce qu'un remplacement approprié soit normalisé.

Donc, dans un avenir prévisible, la codecvtsolution de cette réponse est sûre et portable.


2
Vérifiez avec quel encodage vous enregistrez les fichiers VS
Johann Gerell

9
Sachez qu'il s'agit uniquement de C ++ 11!
bk138 le

1
Dans minGW (gcc / g ++ 4.8.1 et -std = c ++ 11), l'en-tête codecvt n'existe pas. Y a-t-il une alternative?
Brian Jack

1
Pouvez-vous s'il vous plaît fournir un exemple de std::codecvt_utf8pour les débutants
Noitidart

15
Veuillez noter que <codecvt>c'est obsolète depuis 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
Cela ne fonctionne que si tous les caractères sont à un octet, c'est-à-dire ASCII ou ISO-8859-1 . Tout ce qui est multi-octet échouera misérablement, y compris UTF-8. La question contient clairement des caractères multi-octets.
Mark Ransom

28
Cette réponse est clairement insuffisante et ne fait que copier les caractères étroits tels quels en caractères larges. Voir les autres réponses, en particulier celle de Johann Gerell, pour savoir comment passer correctement d'une chaîne codée multi-octets ou utf8 à une chaîne wstring utf16.
DLRdave

10
cette réponse est dangereuse et cassera probablement sur un système non-ascii. c'est-à-dire qu'un nom de fichier arabe sera mutilé par ce hack.
Stephen

9
Cette réponse est utile si vous ignorez la nuance du corps de la question et que vous vous concentrez sur le titre de la question, ce qui m'a amené ici de Google. Tel quel, le titre de la question est extrêmement trompeur et devrait être modifié pour refléter la vraie question posée
Anne Quinn

3
Cela ne fonctionne que pour les caractères ASCII 7 bits. Pour latin1, cela ne fonctionne que si char est configuré comme non signé. Si le type char est signé (ce qui est la plupart du temps le cas), les caractères> 127 donneront des résultats erronés.
huyc

32

Votre question est sous-spécifiée. Strictement, cet exemple est une erreur de syntaxe. Cependant, std::mbstowcsc'est probablement ce que vous recherchez.

C'est une fonction de bibliothèque C et fonctionne sur des tampons, mais voici un idiome facile à utiliser, gracieuseté de TBohne (anciennement 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
chaîne s = "お は よ う"; wchar_t * buf = nouveau wchar_t [s.size ()]; size_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); wstring ws (buf, num_chars); // ws = déformé
Samir

1
@Samir: Vous devez vous assurer que l'encodage d'exécution est le même que l'encodage à la compilation. Vous devrez peut-être setlocaleou ajuster les indicateurs du compilateur. Je ne sais pas parce que je n'utilise pas Windows, mais c'est pourquoi ce n'est pas une fonctionnalité courante. Considérez l'autre réponse si possible.
Potatoswatter le

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

2
@WaffleSouffle C'est dépassé. Des implémentations contiguës sont nécessaires depuis 2011 et les implémentations abandonnent ces astuces bien avant cela.
Potatoswatter

1
et certains environnements comme mingw n'ont toujours pas l'en-tête codecvt, donc certaines des `` meilleures '' solutions plus tôt ne fonctionnent pas, ce qui signifie que ce problème n'a toujours pas de bonnes solutions dans mingw même en décembre 2014
Brian Jack

18

API Windows uniquement, implémentation antérieure à C ++ 11, au cas où quelqu'un en aurait besoin:

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

Vous pouvez l'optimiser. Il n'est pas nécessaire de faire une double copie de la chaîne en utilisant un vector. Réserver simplement les caractères de la chaîne en faisant wstring strW(charsNeeded + 1);puis l' utiliser comme tampon pour la conversion: &strW[0]. Enfin, assurez-vous que le dernier null est présent après la conversion en faisantstrW[charsNeeded] = 0;
c00000fd

1
@ c00000fd, pour autant que je sache, le tampon interne std :: basic_string doit être continu uniquement depuis la norme C ++ 11. Mon code est pré C ++ 11, comme indiqué en haut de l'article. Par conséquent, le code & strW [0] ne serait pas conforme au standard et pourrait légitimement planter au moment de l'exécution.
Alex Che

13

Si vous utilisez Windows / Visual Studio et devez convertir une chaîne en wstring, vous pouvez utiliser:

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

Même procédure pour convertir une chaîne wstring en chaîne (parfois vous devrez spécifier une page de code ):

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

Vous pouvez spécifier une page de code et même UTF8 (c'est assez agréable lorsque vous travaillez avec JNI / Java ). Un moyen standard de convertir un std :: wstring en utf8 std :: string est montré dans cette réponse .

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

Si vous voulez en savoir plus sur les pages de codes, il y a un article intéressant sur Joel sur le logiciel: le minimum absolu que chaque développeur de logiciel doit absolument, positivement savoir sur Unicode et les jeux de caractères .

Ces macros CA2W (Convert Ansi to Wide = unicode) font partie des macros de conversion de chaîne ATL et MFC , exemples inclus.

Parfois, vous devrez désactiver l'avertissement de sécurité # 4995 ', je ne connais pas d'autre solution de contournement (pour moi, cela se produit lorsque j'ai compilé pour WindowsXp dans VS2012).

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

Edit: Eh bien, selon cet article, l'article de Joel semble être: "tout en étant divertissant, il est assez léger sur les détails techniques réels". Article: Ce que tout programmeur doit absolument savoir sur le codage et les jeux de caractères pour travailler avec du texte .


Désolé, je ne suis pas anglophone. Veuillez modifier comme bon vous semble.
lmiguelmh

Que se passe-t-il avec le downvoter? Quel est le problème avec la réponse?
lmiguelmh

Probablement le fait qu'il favorise le code non portable.
Pavel Minaev

Oui, c'est pourquoi j'ai déclaré que cela ne fonctionne que dans Windows / Visual Studio. Mais au moins cette solution est correcte, et pas celle-ci:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh

Remarque supplémentaire: CA2W est sous l'espace de noms d'ATL. (ATL :: CA2W)
Val

12

Voici un moyen de combiner string, wstringet les constantes de chaîne mixtes à wstring. Utilisez la wstringstreamclasse.

Cela ne fonctionne PAS pour les encodages de caractères multi-octets. C'est juste une manière stupide de jeter la sécurité de type et d'étendre les caractères 7 bits de std :: string dans les 7 bits inférieurs de chaque caractère de std: wstring. Cela n'est utile que si vous avez des chaînes ASCII 7 bits et que vous devez appeler une API qui nécessite des chaînes larges.

#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 réponse semble intéressante. Pourriez-vous expliquer un peu: cela fonctionnera-t-il pour les encodages multi-octets, et pourquoi / comment?
wh1t3cat1k

les schémas de codage sont orthogonaux à la classe de stockage. stringstocke les caractères de 1 octet et les caractères de wstring2 octets. quelque chose comme utf8 stocke des caractères multi-octets sous la forme d'une série de valeurs de 1 octet, c'est-à-dire dans un fichier string. les classes de chaînes n'aident pas avec l'encodage. Je ne suis pas un expert de l'encodage des classes en C ++.
Mark Lakata

2
Une raison pour laquelle celle-ci n'est pas la meilleure réponse, étant donné sa simplicité et sa simplicité? Y a-t-il des cas qu'il ne couvre pas?
Ryuu

@MarkLakata, j'ai lu votre réponse au premier commentaire mais je ne suis toujours pas sûr. Cela fonctionnera-t-il pour les caractères multi-octets? En d'autres termes, n'est-elle pas sujette au même écueil que cette réponse ?
Marc 2377

@ Marc.2377 Cela ne fonctionne PAS pour les encodages de caractères multi-octets. C'est juste une façon stupide de jeter la sécurité de type et d'étendre les caractères std::string7 bits des 7 bits inférieurs de chaque caractère de std:wstring. Cela n'est utile que si vous avez des chaînes ASCII 7 bits et que vous devez appeler une API qui nécessite des chaînes larges. Regardez stackoverflow.com/a/8969776/3258851 si vous avez besoin de quelque chose de plus sophistiqué.
Mark Lakata

11

Du char*au wstring:

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

Du stringau wstring:

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

Notez que cela ne fonctionne bien que si la chaîne en cours de conversion ne contient que des caractères ASCII.


7
Parce que cela ne fonctionne que si l'encodage est Windows-1252, qui ne peut même pas contenir les lettres de la question.
Mooing Duck le

3
c'est la manière la moins sujette aux erreurs de le faire, quand vous savez que vous traitez avec ASCII. Ce qui est un cas d'utilisation important lors du portage d'applications vers des API plus récentes.
Sid Sarasvati

Ce n’est pas ainsi. Si vous utilisez Visual Studio, vous devez utiliser atlconv.h. Vérifiez les autres réponses.
lmiguelmh

7

en utilisant Boost.Locale:

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

5

Cette variante est ma préférée dans la vraie vie. Il convertit l'entrée, si elle est valide UTF-8, en la valeur respective wstring. Si l'entrée est corrompue, le wstringest construit à partir des octets uniques. Ceci est extrêmement utile si vous ne pouvez pas vraiment être sûr de la qualité de vos données d'entrée.

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
Je viens de lancer cette question en fonction de votre réponse stackoverflow.com/questions/49669048/… pouvez-vous bien vouloir jeter un coup d'œil
MistyD

2

Si vous avez QT et si vous êtes paresseux pour implémenter une fonction et des choses que vous pouvez utiliser

std :: string str; QString (chaîne) .toStdWString ()


Presque, mais vous devriez simplement commencer par a QString, car le QStringconstructeur ne peut pas accepter une chaîne pour une raison quelconque.
bobsbeenjamin


C'est sympa. De plus, vous pouvez utiliser .c_str () pour laisser QString accepter votre chaîne dans le constructeur.
miep

1

La méthode s2ws fonctionne bien. L'espoir aide.

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
Qu'est-ce que toutes ces réponses allouent de la mémoire dynamique de manière non sécurisée, puis copient les données du tampon vers la chaîne? Pourquoi personne ne se débarrasse de l’intermédiaire dangereux?
Mooing Duck le

hahakubile, pouvez-vous nous aider avec quelque chose de similaire pour ws2s?
cristian le

1

Sur la base de mes propres tests (sur Windows 8, vs2010), les mbstowcs peuvent en fait endommager la chaîne d'origine, cela ne fonctionne qu'avec la page de codes ANSI. Si MultiByteToWideChar / WideCharToMultiByte peut également provoquer une corruption de chaîne - mais ils ont tendance à remplacer les caractères qu'ils ne connaissent pas par '?' points d'interrogation, mais mbstowcs a tendance à s'arrêter lorsqu'il rencontre un caractère inconnu et coupe une chaîne à ce point précis. (J'ai testé des caractères vietnamiens sur des fenêtres finlandaises).

Préférez donc la fonction API Multi * -windows aux fonctions analogiques ansi C.

De plus, ce que j'ai remarqué, le moyen le plus court d'encoder une chaîne d'une page de code à une autre n'est pas d'utiliser les appels de fonction api MultiByteToWideChar / WideCharToMultiByte, mais leurs macros ATL analogiques: W2A / A2W.

Donc, la fonction analogique mentionnée ci-dessus ressemblerait à:

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

_acp est déclaré dans la macro USES_CONVERSION.

Ou aussi fonction qui me manque souvent lors de la conversion d'anciennes données en nouvelles:

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

   _acp = CP_UTF8;
   return W2A( pw );
}

Mais veuillez noter que ces macros utilisent fortement la pile - ne pas utiliser pour les boucles ou les boucles récursives pour la même fonction - après avoir utilisé la macro W2A ou A2W - mieux vaut retourner ASAP, donc la pile sera libérée de la conversion temporaire.


1

Chaîne en wstring

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 en chaîne

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
Ce Str2Wstr a un problème avec la terminaison 0. Il n'est plus possible de concaténer les wstrings générés via "+" (comme dans wstring s3 = s1 + s2). Je publierai bientôt une réponse résolvant ce problème. Vous devez d'abord tester les fuites de mémoire.
thewhiteambit le

-2

string s = "おはよう"; est une erreur.

Vous devez utiliser wstring directement:

wstring ws = L"おはよう";

1
Cela ne fonctionnera pas non plus. Vous devrez convertir ces caractères non BMP en séquences d'échappement C.
Dave Van den Eynde

3
@Dave: cela fonctionne si votre compilateur prend en charge l'unicode dans les fichiers source, et tous ceux de la dernière décennie le font (visual studio, gcc, ...)
Thomas Bonini

Salut, quel que soit le codage système par défaut (je peux avoir l'arabe comme codage système par défaut par exemple), quel devrait être le codage du fichier de code source pour que L "お は よ う" fonctionne? devrait-il être en UTF-16, ou puis-je avoir UTF-8 sans BOM pour le codage de fichier .cpp?
Afriza N.Arief

2
@afriza: cela n'a pas vraiment d'importance tant que votre compilation le supporte
Thomas Bonini

2
Ce n'est pas une erreur; les caractères étendus dans une chaîne "étroite" sont définis pour être mappés à des séquences multi-octets. Le compilateur devrait le supporter aussi longtemps que le système d'exploitation le fait, ce qui est le moins que vous puissiez demander.
Potatoswatter

-2

utilisez ce code pour convertir votre chaîne en 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
Notez que la question ne mentionne pas Windows et que cette réponse est uniquement Windows.
Johann Gerell

CP_ACPest certainement le mauvais argument. Tout à coup, l'état de l'environnement du thread en cours d'exécution a un effet sur le comportement du code. Pas conseillé. Spécifiez un codage de caractères fixe dans votre conversion. (Et pensez à gérer les erreurs.)
IInspectable
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.