web-dev-qa-db-fra.com

Convertir entre chaîne, chaîne u16 et chaîne u32

Je cherchais un moyen de convertir entre les types de chaînes Unicode et suis tombé sur cette méthode . Non seulement je ne comprends pas complètement la méthode (il n'y a pas de commentaires) mais aussi l'article implique qu'à l'avenir il y aura de meilleures méthodes.

Si c'est la meilleure méthode, pourriez-vous s'il vous plaît indiquer ce qui la fait fonctionner, et sinon je voudrais entendre des suggestions pour de meilleures méthodes.

43
DrYap

mbstowcs() et wcstombs() ne se convertissent pas nécessairement en UTF-16 ou UTF-32, ils se convertissent en wchar_t et quel que soit le codage local wchar_t . Tous les paramètres régionaux Windows utilisent un codage à deux octets wchar_t Et UTF-16 comme codage, mais les autres plates-formes principales utilisent un codage à quatre octets wchar_t Avec UTF-32 (ou même un codage non Unicode pour certains paramètres régionaux). Une plate-forme qui ne prend en charge que les codages à un octet pourrait même avoir un octet wchar_t Et avoir un codage différent selon les paramètres régionaux. Donc wchar_t Me semble être un mauvais choix pour la portabilité et Unicode. *

De meilleures options ont été introduites dans C++ 11; de nouvelles spécialisations de std :: codecvt, de nouvelles classes de codecvt et un nouveau modèle pour rendre leur utilisation pour les conversions très pratique.

Tout d'abord, la nouvelle classe de modèle pour l'utilisation de codecvt est std :: wstring_convert. Une fois que vous avez créé une instance d'une classe std :: wstring_convert, vous pouvez facilement convertir entre les chaînes:

std::wstring_convert<...> convert; // ... filled in with a codecvt to do UTF-8 <-> UTF-16
std::string utf8_string = u8"This string has UTF-8 content";
std::u16string utf16_string = convert.from_bytes(utf8_string);
std::string another_utf8_string = convert.to_bytes(utf16_string);

Pour effectuer une conversion différente, vous avez juste besoin de différents paramètres de modèle, dont l'un est une facette codecvt. Voici quelques nouvelles facettes faciles à utiliser avec wstring_convert:

std::codecvt_utf8_utf16<char16_t> // converts between UTF-8 <-> UTF-16
std::codecvt_utf8<char32_t> // converts between UTF-8 <-> UTF-32
std::codecvt_utf8<char16_t> // converts between UTF-8 <-> UCS-2 (warning, not UTF-16! Don't bother using this one)

Exemples d'utilisation de ceux-ci:

std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::string a = convert.to_bytes(u"This string has UTF-16 content");
std::u16string b = convert.from_bytes(u8"blah blah blah");

Les nouvelles spécialisations std :: codecvt sont un peu plus difficiles à utiliser car elles ont un destructeur protégé. Pour contourner cela, vous pouvez définir une sous-classe qui a un destructeur, ou vous pouvez utiliser la fonction de modèle std :: use_facet pour obtenir une instance de codecvt existante. En outre, un problème avec ces spécialisations est que vous ne pouvez pas les utiliser dans Visual Studio 2010 car la spécialisation de modèle ne fonctionne pas avec les types typedef'd et que le compilateur définit char16_t et char32_t comme typedefs. Voici un exemple de définition de votre propre sous-classe de codecvt:

template <class internT, class externT, class stateT>
struct codecvt : std::codecvt<internT,externT,stateT>
{ ~codecvt(){} };

std::wstring_convert<codecvt<char16_t,char,std::mbstate_t>,char16_t> convert16;
std::wstring_convert<codecvt<char32_t,char,std::mbstate_t>,char32_t> convert32;

La spécialisation char16_t convertit entre UTF-16 et UTF-8. La spécialisation char32_t, UTF-32 et UTF-8.

Notez que ces nouvelles conversions fournies par C++ 11 n'incluent aucun moyen de convertir directement entre UTF-32 et UTF-16. Au lieu de cela, il vous suffit de combiner deux instances de std :: wstring_convert.


***** J'ai pensé ajouter une note sur wchar_t et son but, pour souligner pourquoi il ne devrait généralement pas être utilisé pour Unicode ou du code internationalisé portable. Ce qui suit est une version courte de ma réponse https://stackoverflow.com/a/11107667/365496

Qu'est-ce que wchar_t?

wchar_t est défini de telle sorte que le codage de n'importe quel paramètre régional puisse être converti en wchar_t où chaque wchar_t représente exactement un point de code:

Le type wchar_t est un type distinct dont les valeurs peuvent représenter des codes distincts pour tous les membres du plus grand jeu de caractères étendu spécifié parmi les paramètres régionaux pris en charge (22.3.1). - [basic.fundamental] 3.9.1/5

Cela n'exige pas que wchar_t soit suffisamment grand pour représenter simultanément n'importe quel caractère de tous les paramètres régionaux. Autrement dit, l'encodage utilisé pour wchar_t peut différer entre les paramètres régionaux. Ce qui signifie que vous ne pouvez pas nécessairement convertir une chaîne en wchar_t en utilisant un paramètre régional, puis reconvertir en char en utilisant un autre paramètre régional.

Étant donné que cela semble être l'utilisation principale dans la pratique pour wchar_t, vous vous demandez peut-être à quoi cela sert, sinon.

L'intention et le but d'origine de wchar_t étaient de simplifier le traitement de texte en le définissant de telle sorte qu'il nécessite un mappage un à un entre les unités de code d'une chaîne et les caractères du texte, permettant ainsi l'utilisation des mêmes algorithmes simples utilisés avec les chaînes ascii. pour travailler avec d'autres langues.

Malheureusement, les exigences de wchar_t supposent un mappage un à un entre les caractères et les points de code pour y parvenir. Unicode rompt cette hypothèse, vous ne pouvez donc pas utiliser wchar_t en toute sécurité pour des algorithmes de texte simples.

Cela signifie que les logiciels portables ne peuvent pas utiliser wchar_t soit comme représentation commune de texte entre des paramètres régionaux, ni pour permettre l'utilisation d'algorithmes de texte simples.

À quoi sert wchar_t aujourd'hui?

Pas grand-chose, pour le code portable de toute façon. Si __STDC_ISO_10646__ Est défini, les valeurs de wchar_t représentent directement les points de code Unicode avec les mêmes valeurs dans tous les paramètres régionaux. Cela permet de faire en toute sécurité les conversions inter-locales mentionnées précédemment. Cependant, vous ne pouvez pas vous y fier uniquement pour décider que vous pouvez utiliser wchar_t de cette façon car, alors que la plupart des plates-formes Unix le définissent, Windows ne le fait pas même si Windows utilise les mêmes paramètres régionaux wchar_t dans tous les paramètres régionaux.

La raison pour laquelle Windows ne définit pas __STDC_ISO_10646__ Je pense que c'est parce que Windows utilise UTF-16 comme son encodage wchar_t, et parce que UTF-16 utilise des paires de substitution pour représenter les points de code supérieurs à U + FFFF, ce qui signifie que UTF-16 ne satisfait pas aux exigences de __STDC_ISO_10646__.

Pour le code spécifique à la plate-forme, wchar_t peut être plus utile. C'est essentiellement requis sous Windows (par exemple, certains fichiers ne peuvent tout simplement pas être ouverts sans utiliser les noms de fichiers wchar_t), bien que Windows soit la seule plate-forme où cela est vrai pour autant que je sache (alors peut-être pouvons-nous penser à wchar_t comme `` Windows_char_t '').

Avec le recul, wchar_t n'est clairement pas utile pour simplifier la gestion du texte ou comme stockage pour du texte indépendant des paramètres régionaux. Le code portable ne doit pas tenter de l'utiliser à ces fins.

87
bames53

J'ai écrit des fonctions d'aide pour convertir vers/à partir de chaînes UTF8 (C++ 11):

#include <string>
#include <locale>
#include <codecvt>

using namespace std;

template <typename T>
string toUTF8(const basic_string<T, char_traits<T>, allocator<T>>& source)
{
    string result;

    wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
    result = convertor.to_bytes(source);

    return result;
}

template <typename T>
void fromUTF8(const string& source, basic_string<T, char_traits<T>, allocator<T>>& result)
{
    wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
    result = convertor.from_bytes(source);
}

Exemple d'utilisation:

// Unicode <-> UTF8
{
    wstring uStr = L"Unicode string";
    string str = toUTF8(uStr);

    wstring after;
    fromUTF8(str, after);
    assert(uStr == after);
}

// UTF16 <-> UTF8
{
    u16string uStr;
    uStr.Push_back('A');
    string str = toUTF8(uStr);

    u16string after;
    fromUTF8(str, after);
    assert(uStr == after);
}
12
dimon4eg