Conversion entre string, u16string & u32string

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

Si c'est la meilleure méthode, pourriez-vous nous préciser ce qui fait que cela fonctionne, et si non, j'aimerais entendre vos suggestions pour l'amélioration des méthodes.

40
demandé sur Xeo 2011-08-29 20:09:17

3 réponses

mbstowcs() et wcstombs() ne se convertissent pas nécessairement en UTF-16 ou UTF-32, Ils se convertissent en wchar_t et quelles que soient les paramètres régionaux wchar_t encodage. Toutes les locales Windows utilisent un byte de deux wchar_t et UTF-16 comme encodage, mais les autres grandes plateformes utilisent un 4 octets wchar_t avec UTF-32 (ou même un encodage non Unicode pour certaines locales). Une plate-forme qui ne supporte que des encodages à un octet pourrait même avoir un octet wchar_t et avoir un encodage différent selon la localisation. Donc wchar_t me semble être un mauvais choix pour la portabilité et Unicode. *

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

tout d'abord, la nouvelle classe template 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 strings:

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 faire différentes conversion vous avez juste besoin de différents paramètres du modèle, dont l'un est une facette codecvt. Voici de nouvelles facettes qui sont 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 ces:

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 un codecvt existant. instance. En outre, un problème avec ces spécialisations est que vous ne pouvez pas les utiliser dans Visual Studio 2010 parce que la spécialisation template 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 ne inclure n'importe quelle façon de convertir directement entre UTF-32 et UTF-16. Vous n'avez qu'à 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 pas généralement être utilisé pour Unicode ou le 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 caractères de n'importe quelle locale puisse être converti en wchar_t où chaque wchar_t représente exactement un point de code:

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 locales supportées (22.3.1). -- [base.fondamental] 3.9.1 / 5

ne pas exiger que wchar_t soit assez grand pour représenter n'importe quel personnage de tous les lieux simultanément. C'est-à-dire que l'encodage utilisé pour wchar_t peut différer d'une localité à l'autre. Ce qui signifie que vous ne pouvez pas nécessairement convertir une chaîne de caractères en wchar_t en utilisant une locale et ensuite convertir de nouveau en char en utilisant une autre locale.

puisque cela semble être l'usage principal dans la pratique pour wchar_t vous pourriez vous demander ce que c'est bon si ce n'est pas cela.

à l'origine, l'intention et le but de wchar_t étaient de rendre le traitement de texte simple en le définissant ainsi. qu'il nécessite une mise en correspondance des unités de code d'une chaîne avec 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 sur wchar_t supposent un mappage un-à-un entre les caractères et les codépoints pour atteindre ceci. Unicode brise cette hypothèse, donc vous ne pouvez pas utiliser wchar_t en toute sécurité pour des algorithmes de texte simples non plus.

Cela signifie que le logiciel portable ne peut pas utiliser wchar_t soit comme une représentation commune du texte entre les locales, soit pour permettre l'utilisation d'algorithmes de texte simples.

à quoi sert wchar_t aujourd'hui?

pas grand chose, pour le code portable en tout cas. Si __STDC_ISO_10646__ est défini alors les valeurs de wchar_t représentent directement les codépoints Unicode avec les mêmes valeurs dans toutes les locales. Cela permet d'effectuer en toute sécurité les conversions inter-locales mentionnées plus haut. Cependant, vous ne pouvez pas vous fier uniquement à elle pour décider que vous pouvez utiliser wchar_t de cette façon parce que, alors que la plupart des plates-formes unix le définissent, Windows ne le fait pas même si Windows utilise la même locale wchar_t dans toutes les locales.

la raison pour laquelle Windows ne définit pas __STDC_ISO_10646__ je pense que C'est parce que Windows utilise UTF-16 comme encodage wchar_t, et parce que UTF-16 utilise des paires de substituts pour représenter des codépoints supérieurs à U+FFFF, ce qui signifie que UTF-16 ne satisfait pas les exigences pour __STDC_ISO_10646__.

pour le code spécifique à la plate-forme, wchar_t peut être plus utile. Il s'agit essentiellement d' requis sur 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 (donc peut-être que nous pouvons penser à wchar_t comme 'Windows_char_t').

avec le recul, wchar_t n'est manifestement pas utile pour simplifier la manipulation de texte ou pour stocker du texte local indépendant. Code Portable ne devrait pas tenter de l'utiliser à ces fins.

84
répondu bames53 2017-05-23 12:10:41

j'ai écrit des fonctions d'aide pour convertir en / à 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
répondu dimon4eg 2016-01-16 13:24:52
mbstowcs (Multi-Octets de Large chaîne de caractères), et à l'inverse, wcstombs.

si vous avez besoin de UTF-32 aussi, vous avez besoin de iconv, qui est en POSIX 2001 mais pas en standard C, donc sur Windows vous aurez besoin d'un remplacement comme libiconv.

voici un exemple d'utilisation mbstowcs:

#include <string>
#include <iostream>
#include <stdlib.h>

using namespace std;

wstring widestring(const string &text);

int main()
{
  string text;
  cout << "Enter something: ";
  cin >> text;

  wcout << L"You entered " << widestring(text) << ".\n";
  return 0;
}

wstring widestring(const string &text)
{
  wstring result;
  result.resize(text.length());
  mbstowcs(&result[0], &text[0], text.length());
  return result;
}

L'inverse qui va comme ceci:

string mbstring(const wstring &text)
{
  string result;
  result.resize(text.length());
  wcstombs(&result[0], &text[0], text.length());
  return result;
}

Pinaille: Oui, je sais, la taille de wchar_t est définie par l'implémentation, de sorte qu'il be 4 octets (UTF-32). Cependant, je ne connais pas de compilateur qui fait ça.

-2
répondu Raphael R. 2011-08-29 16:31:13