Est-il possible de convertir une chaîne UTF8 dans un std :: string en std :: wstring et vice versa de manière indépendante de la plate-forme? 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 standard C ++.
Réponses
Trop de publicités?J'ai posé cette question il y a 5 ans. Ce fil a été très utile pour moi, j'en suis venu à une conclusion, puis j'ai avancé sur mon projet. C'est drôle que j'ai besoin de quelque chose de similaire récemment, totalement étrangers à ce projet dans le passé. Comme je faisais des recherches pour trouver des solutions possibles, je suis tombé sur ma propre question :)
La solution que j'ai choisi est maintenant basé sur le C++11. Les bibliothèques boost que Constantin mentionne dans sa réponse sont désormais partie de la norme. Si on remplace les std::wstring avec la nouvelle chaîne de type std::u16string, puis les conversions ressemblera à 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 pour de l'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 on le voit à partir d'autres réponses, il existe de nombreuses approches à ce problème. C'est pourquoi je m'abstiens de sélection d'une accepté de répondre.
Allez, les gens - la définition du problème, qui stipule explicitement que les 8 bits de codage de caractères UTF-8. C'est un banal problème; si vous l'avez écrit vous-même, le code ne serait probablement pas plus d'une douzaine de lignes.
Il suffit de regarder les codages sur ces pages de Wikipédia pour de l'UTF-8 et UTF-16.
Edit: j'ai peut-être été coupable d'un peu d'exagération. Certes, le code est bien plus qu'une dizaine de lignes; j'ai peu-tourner toute ma vie, alors peut-être que cela semble plus simple pour moi que ça l'est vraiment.
Le principe est simple: passer par l'entrée et à assembler un 32-bit de point de code Unicode selon un UTF spec, puis émet le point de code, selon les autres spec. L'individu points de code ont pas besoin de traduction, qu'il serait nécessaire avec un autre codage de caractères; c'est ce qui rend ce un problème simple.
Voici une mise en œuvre rapide de l'UTF-16 de conversion en UTF-8 et vice versa. Il suppose que l'entrée est déjà codé correctement - le vieux dicton: "Garbage in, Garbage out" s'applique ici. Je crois que la vérification de l'encodage est mieux de le faire comme une étape distincte. Notez ce code n'est que très peu testé.
std::string UTF16to8(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;
}
std::wstring UTF8to16(const char * in)
{
std::wstring out;
unsigned int codepoint = 0;
int following = 0;
for (in; *in != 0; ++in)
{
unsigned char ch = *in;
if (ch <= 0x7f)
{
codepoint = ch;
following = 0;
}
else if (ch <= 0xbf)
{
if (following > 0)
{
codepoint = (codepoint << 6) | (ch & 0x3f);
--following;
}
}
else if (ch <= 0xdf)
{
codepoint = ch & 0x1f;
following = 1;
}
else if (ch <= 0xef)
{
codepoint = ch & 0x0f;
following = 2;
}
else
{
codepoint = ch & 0x07;
following = 3;
}
if (following == 0)
{
if (codepoint > 0xffff)
{
out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
}
else
out.append(1, static_cast<wchar_t>(codepoint));
codepoint = 0;
}
}
return out;
}
Edit 2: Voici une version améliorée de l' UTF8to16
. Il ne gère pas mal formés d'entrée très bien, mais il n'émettent que des bêtises au lieu de l'invalidité de l'UTF-16 séquences. C'est beaucoup plus simple que l'original, et peut-être plus rapide aussi.
std::wstring UTF8to16(const char * in)
{
std::wstring out;
if (in == NULL)
return 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 (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;
}
Vous pouvez extraire utf8_codecvt_facet
de la bibliothèque de sérialisation Boost .
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);
}
Recherchez les fichiers utf8_codecvt_facet.hpp
et utf8_codecvt_facet.cpp
dans les sources boost.
Il y a plusieurs façons de le faire, mais les résultats dépendent de ce que les encodages de caractères sont dans l' string
et wstring
variables.
Si vous connaissez l' string
est en ASCII, vous pouvez simplement utiliser wstring
s'itérateur constructeur:
string s = "This is surely ASCII.";
wstring w(s.begin(), s.end());
Si votre string
a un autre encodage, cependant, vous aurez de très mauvais résultats. Si le codage Unicode, vous pouvez prendre un coup d'oeil à l' ICU projet, qui prévoit une croix-plate-forme ensemble de bibliothèques convertir vers et à partir de toutes sortes de codages Unicode.
Si votre string
contient des caractères dans une page de code, alors peut $DIVINITÉ avoir pitié de votre âme.