Est-il possible de convertir une chaîne UTF8 dans une chaîne std :: en std :: wstring et vice versa d'une manière indépendante de la plateforme? Dans une application Windows, j'utiliserais MultiByteToWideChar et WideCharToMultiByte. Cependant, le code est compilé pour plusieurs systèmes d'exploitation et je suis limité à la bibliothèque C++ standard.
J'ai posé cette question il y a 5 ans. Ce fil m'a été très utile à l'époque, je suis arrivé à une conclusion, puis j'ai poursuivi mon projet. C'est drôle que j'avais besoin de quelque chose de similaire récemment, sans aucun rapport avec ce projet du passé. Alors que je cherchais des solutions possibles, je suis tombé sur ma propre question :)
La solution que j'ai choisie maintenant est basée sur C++ 11. Les bibliothèques de boost que Constantin mentionne dans sa réponse font maintenant partie de la norme. Si nous remplaçons std :: wstring par le nouveau type de chaîne std :: u16string, les conversions ressembleront à ceci:
UTF-8 à UTF-16
std::string source;
...
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::u16string dest = convert.from_bytes(source);
UTF-16 à UTF-8
std::u16string source;
...
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::string dest = convert.to_bytes(source);
Comme le montrent les autres réponses, il existe plusieurs approches au problème. C'est pourquoi je m'abstiens de choisir une réponse acceptée.
Vous pouvez extraire utf8_codecvt_facet
de Boost bibliothèque de sérialisation .
Leur exemple d'utilisation:
typedef wchar_t ucs4_t;
std::locale old_locale;
std::locale utf8_locale(old_locale,new utf8_codecvt_facet<ucs4_t>);
// Set a New global locale
std::locale::global(utf8_locale);
// Send the UCS-4 data out, converting to UTF-8
{
std::wofstream ofs("data.ucd");
ofs.imbue(utf8_locale);
std::copy(ucs4_data.begin(),ucs4_data.end(),
std::ostream_iterator<ucs4_t,ucs4_t>(ofs));
}
// Read the UTF-8 data back in, converting to UCS-4 on the way in
std::vector<ucs4_t> from_file;
{
std::wifstream ifs("data.ucd");
ifs.imbue(utf8_locale);
ucs4_t item = 0;
while (ifs >> item) from_file.Push_back(item);
}
Chercher utf8_codecvt_facet.hpp
et utf8_codecvt_facet.cpp
fichiers dans les sources boost.
La définition du problème indique explicitement que le codage de caractères à 8 bits est UTF-8. Cela en fait un problème trivial; tout ce qu'il faut, c'est un peu de twiddling pour convertir d'une spécification UTF en une autre.
Regardez simplement les encodages sur ces pages Wikipedia pour TF-8 , TF-16 , et TF-32 .
Le principe est simple - passez par l'entrée et assemblez un point de code Unicode 32 bits selon une spécification UTF, puis émettez le point de code selon l'autre spécification. Les points de code individuels n'ont pas besoin de traduction, comme cela serait requis avec tout autre codage de caractères; c'est ce qui en fait un problème simple.
Voici une implémentation rapide de la conversion de wchar_t
En UTF-8 et vice versa. Il suppose que l'entrée est déjà correctement encodée - le vieil adage "Garbage in, garbage out" s'applique ici. Je crois que la vérification de l'encodage est préférable de faire une étape distincte.
std::string wchar_to_UTF8(const wchar_t * in)
{
std::string out;
unsigned int codepoint = 0;
for (in; *in != 0; ++in)
{
if (*in >= 0xd800 && *in <= 0xdbff)
codepoint = ((*in - 0xd800) << 10) + 0x10000;
else
{
if (*in >= 0xdc00 && *in <= 0xdfff)
codepoint |= *in - 0xdc00;
else
codepoint = *in;
if (codepoint <= 0x7f)
out.append(1, static_cast<char>(codepoint));
else if (codepoint <= 0x7ff)
{
out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)));
out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
}
else if (codepoint <= 0xffff)
{
out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)));
out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
}
else
{
out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)));
out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
}
codepoint = 0;
}
}
return out;
}
Le code ci-dessus fonctionne pour les entrées UTF-16 et UTF-32, simplement parce que la plage d800
À dfff
sont des points de code non valides; ils indiquent que vous décodez UTF-16. Si vous savez que wchar_t
Est de 32 bits, vous pouvez supprimer du code pour optimiser la fonction.
std::wstring UTF8_to_wchar(const char * in)
{
std::wstring out;
unsigned int codepoint;
while (*in != 0)
{
unsigned char ch = static_cast<unsigned char>(*in);
if (ch <= 0x7f)
codepoint = ch;
else if (ch <= 0xbf)
codepoint = (codepoint << 6) | (ch & 0x3f);
else if (ch <= 0xdf)
codepoint = ch & 0x1f;
else if (ch <= 0xef)
codepoint = ch & 0x0f;
else
codepoint = ch & 0x07;
++in;
if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
{
if (sizeof(wchar_t) > 2)
out.append(1, static_cast<wchar_t>(codepoint));
else if (codepoint > 0xffff)
{
out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
}
else if (codepoint < 0xd800 || codepoint >= 0xe000)
out.append(1, static_cast<wchar_t>(codepoint));
}
}
return out;
}
Encore une fois, si vous savez que wchar_t
Est de 32 bits, vous pouvez supprimer du code de cette fonction, mais dans ce cas, cela ne devrait faire aucune différence. L'expression sizeof(wchar_t) > 2
est connue au moment de la compilation, donc tout compilateur décent reconnaîtra le code mort et le supprimera.
Il existe plusieurs façons de procéder, mais les résultats dépendent de ce que sont les encodages de caractères dans les variables string
et wstring
.
Si vous savez que string
est ASCII, vous pouvez simplement utiliser le constructeur d'itérateur de wstring
:
string s = "This is surely ASCII.";
wstring w(s.begin(), s.end());
Cependant, si votre string
a un autre encodage, vous obtiendrez de très mauvais résultats. Si l'encodage est Unicode, vous pouvez jeter un œil au projet IC , qui fournit un ensemble multiplateforme de bibliothèques qui convertissent vers et depuis toutes sortes d'encodages Unicode.
Si votre string
contient des caractères dans une page de codes, alors $ DEITY peut avoir pitié de votre âme.
Nous remercions bames5 pour avoir fourni des versions mises à jour
Vous pouvez utiliser la facette codecvt
locale . Une spécialisation spécifique est définie, codecvt<wchar_t, char, mbstate_t>
qui peut vous être utile, bien que son comportement soit spécifique au système et ne garantisse aucune conversion en UTF-8.
TFConverter - consultez cette bibliothèque. Il fait une telle conversion, mais vous avez également besoin de la classe ConvertUTF - je l'ai trouvé ici
J'ai créé ma propre bibliothèque pour la conversion d'utf-8 en utf-16/utf-32 - mais j'ai décidé de créer un fork du projet existant à cet effet.
https://github.com/tapika/cutf
(Originaire de https://github.com/noct/cutf )
L'API fonctionne aussi bien en C simple qu'en C++.
Les prototypes de fonctions ressemblent à ceci: (Pour la liste complète, voir https://github.com/tapika/cutf/blob/master/cutf.h )
//
// Converts utf-8 string to wide version.
//
// returns target string length.
//
size_t utf8towchar(const char* s, size_t inSize, wchar_t* out, size_t bufSize);
//
// Converts wide string to utf-8 string.
//
// returns filled buffer length (not string length)
//
size_t wchartoutf8(const wchar_t* s, size_t inSize, char* out, size_t outsize);
#ifdef __cplusplus
std::wstring utf8towide(const char* s);
std::wstring utf8towide(const std::string& s);
std::string widetoutf8(const wchar_t* ws);
std::string widetoutf8(const std::wstring& ws);
#endif
Exemple d'utilisation/application de test simple pour les tests de conversion utf:
#include "cutf.h"
#define ok(statement) \
if( !(statement) ) \
{ \
printf("Failed statement: %s\n", #statement); \
r = 1; \
}
int simpleStringTest()
{
const wchar_t* chineseText = L"主体";
auto s = widetoutf8(chineseText);
size_t r = 0;
printf("simple string test: ");
ok( s.length() == 6 );
uint8_t utf8_array[] = { 0xE4, 0xB8, 0xBB, 0xE4, 0xBD, 0x93 };
for(int i = 0; i < 6; i++)
ok(((uint8_t)s[i]) == utf8_array[i]);
auto ws = utf8towide(s);
ok(ws.length() == 2);
ok(ws == chineseText);
if( r == 0 )
printf("ok.\n");
return (int)r;
}
Et si cette bibliothèque ne répond pas à vos besoins, n'hésitez pas à ouvrir le lien suivant:
et faites défiler vers le bas à la fin de la page et choisissez n'importe quelle bibliothèque plus lourde que vous aimez.