828 votes

std::wstring VS std::string

Je ne suis pas capable de comprendre les différences entre std::string et std::wstring . Je sais wstring prend en charge les caractères larges tels que les caractères Unicode. J'ai les questions suivantes :

  1. Quand dois-je utiliser std::wstring sur std::string ?
  2. Can std::string contient la totalité du jeu de caractères ASCII, y compris les caractères spéciaux ?
  3. Est std::wstring supporté par tous les compilateurs C++ courants ?
  4. Qu'est-ce qu'un " caractère étendu " ?

1079voto

paercebal Points 38526

string ? wstring ?

std::string est un basic_string modélisé sur un char et std::wstring sur un wchar_t .

char vs. wchar_t

char est censé contenir un caractère, généralement un caractère d'un octet. wchar_t est censé contenir un caractère large, et ensuite, les choses se compliquent : sous Linux, un caractère de type wchar_t est de 4 octets, alors que sous Windows, c'est 2-bytes

qu'en est-il Unicode alors ?

Le problème est que ni l'un ni l'autre char ni wchar_t est directement liée à l'unicode.

Sous Linux ?

Prenons un système d'exploitation Linux : Mon système Ubuntu est déjà conscient de l'unicode. Lorsque je travaille avec une chaîne de caractères, elle est codée nativement en format UTF-8 (c'est-à-dire une chaîne de caractères Unicode). Le code suivant :

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;
   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(binary)    :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(static_cast<unsigned char>(text[i])) ;
   }

   std::cout << std::endl << std::endl ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(binary)   :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(static_cast<unsigned short>(wtext[i])) ;
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

sort le texte suivant :

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(binary)    : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(binary)   : 111 108 233

Vous verrez le texte "olé" en char est en réalité construit par quatre caractères : 110, 108, 195 et 169 (sans compter le zéro de queue). (Je vous laisse étudier le wchar_t à titre d'exercice)

Ainsi, lorsque vous travaillez avec un caractère sous Linux, vous finissez généralement par utiliser Unicode sans même le savoir. Et comme std::string fonctionne avec les chars, std::string est donc déjà prêt pour l'unicode.

Notez que std::string, comme l'API des chaînes de caractères en C, considérera la chaîne "olé" comme ayant 4 caractères, et non trois. Vous devez donc être prudent lorsque vous tronquez/jouez avec des caractères unicodes car certaines combinaisons de caractères sont interdites en UTF-8.

Sous Windows ?

Sous Windows, c'est un peu différent. Win32 a dû prendre en charge un grand nombre d'applications fonctionnant avec char et sur différents charsets / codepages produit dans le monde entier, avant l'avènement d'Unicode.

Leur solution était donc intéressante : Si une application fonctionne avec char Les chaînes de caractères sont alors codées/imprimées/affichées sur les étiquettes de l'interface graphique en utilisant le jeu de caractères/codepage local de la machine. Par exemple, "olé" sera "olé" dans un Windows localisé en français, mais sera quelque chose de différent dans un Windows localisé en cyrillique ("olй" si vous utilisez le codage de la langue française). Windows-1251 ). Ainsi, les "applications historiques" fonctionnent généralement toujours de la même manière.

Pour les applications basées sur Unicode, Windows utilise wchar_t qui a une largeur de 2 octets et qui est codée dans le format UTF-16 Unicode, qui est codé sur des caractères de 2 octets (ou au moins, le plus compatible UCS-2, qui est presque la même chose IIRC).

Applications utilisant char sont dits "multi-octets" (parce que chaque glyphe est composé d'un ou de plusieurs char ), tandis que les applications utilisant wchar_t sont dites "larges" (parce que chaque glyphe est composé d'un ou de deux wchar_t . Voir MultiByteToWideChar et WideCharToMultiByte API de conversion Win32 pour plus d'informations.

Ainsi, si vous travaillez sous Windows, vous veulent absolument à utiliser wchar_t (à moins que vous n'utilisiez un cadre cachant cela, comme GTK+ ou QT ...). Le fait est que, dans les coulisses, Windows travaille avec les éléments suivants wchar_t chaînes de caractères, de sorte que même les applications historiques auront leurs char chaînes de caractères converties en wchar_t lors de l'utilisation d'API telles que SetWindowText (fonction API de bas niveau permettant de définir l'étiquette sur une interface graphique Win32).

Des problèmes de mémoire ?

UTF-32 est de 4 octets par caractères, il n'y a donc pas grand chose à ajouter, si ce n'est qu'un texte UTF-8 et un texte UTF-16 utiliseront toujours moins ou la même quantité de mémoire qu'un texte UTF-32 (et généralement moins).

S'il y a un problème de mémoire, sachez que pour la plupart des langues occidentales, le texte UTF-8 utilisera moins de mémoire que le même texte UTF-16.

Cependant, pour les autres langues (chinois, japonais, etc.), la mémoire utilisée sera soit la même, soit plus grande pour UTF-8 que pour UTF-16.

En somme, l'UTF-16 utilisera le plus souvent 2 octets par caractères (à moins que vous n'ayez affaire à des glyphes d'une langue ésotérique (klingon ? elfique ?), tandis que l'UTF-8 dépensera de 1 à 4 octets.

Voir http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 pour plus d'informations.

Conclusion

1. Quand dois-je utiliser std::wstring plutôt que std::string ?

Sur Linux ? Presque jamais (§).
Sur Windows ? Presque toujours (§).
Sur le code multiplateforme ? Cela dépend de votre boîte à outils...

(§) : sauf si vous utilisez un toolkit/framework disant le contraire

2. std::string peut-il contenir tout le jeu de caractères ASCII, y compris les caractères spéciaux ?

Avis : Une std::string convient pour contenir un tampon 'binaire', alors qu'une std::wstring ne convient pas !

Sur Linux ? Oui.
Sous Windows ? Seuls les caractères spéciaux disponibles pour la locale actuelle de l'utilisateur de Windows.

Edit (Après un commentaire de Johann Gerell ) : un std::string sera suffisant pour gérer toutes les chaînes de caractères (chaque caractère étant un nombre de 0 à 255). Mais :

  1. L'ASCII est censé aller de 0 à 127. Les caractères supérieurs ne sont PAS ASCII.
  2. un caractère de 0 à 127 sera maintenu correctement
  3. un caractère de 128 à 255 aura une signification qui dépend de votre encodage (unicode, non-unicode, etc.), mais il pourra contenir tous les glyphes Unicode tant qu'ils sont encodés en UTF-8.

3. std::wstring est-il supporté par presque tous les compilateurs C++ courants ?

La plupart du temps, à l'exception des compilateurs basés sur GCC qui sont portés sur Windows.
Cela fonctionne sur mon g++ 4.3.2 (sous Linux), et j'ai utilisé l'API Unicode sur Win32 depuis Visual C++ 6.

4. Qu'est-ce qu'un caractère large ?

En C/C++, c'est un type de caractère qui s'écrit wchar_t qui est plus grande que la simple char type de caractère. Il est censé être utilisé pour mettre à l'intérieur des caractères dont les indices (comme les glyphes Unicode) sont supérieurs à 255 (ou 127, selon...)

93voto

Pavel Radzivilovsky Points 11613
  1. Quelques faibles raisons. Il existe pour des raisons historiques, où l'on croyait que les caractères larges étaient la bonne façon de supporter l'Unicode. Il est maintenant utilisé pour interfacer les API qui préfèrent les chaînes UTF-16. Je ne les utilise qu'à proximité directe de tels appels d'API.
  2. Cela n'a rien à voir avec std::string. Elle peut contenir n'importe quel encodage que vous y mettez. La seule question est de savoir comment Vous traiter son contenu. Je recommande l'UTF-8, afin qu'il puisse contenir correctement tous les caractères unicode. C'est une pratique courante sous Linux, mais je pense que les programmes Windows devraient également le faire.
  3. Non.
  4. Le nom du caractère large prête à confusion. Aux premiers jours d'Unicode, on pensait que le caractère pouvait être codé sur deux octets, d'où son nom. Aujourd'hui, il signifie "toute partie du caractère qui fait deux octets". L'UTF-16 est considéré comme une séquence de telles paires d'octets (alias caractères larges). Un caractère dans l'UTF-16 prend soit une ou deux pares.

Pour plus d'informations, veuillez consulter le site http://www.utf8everywhere.org

39voto

Frunsi Points 4550

Ainsi, chaque lecteur ici présent devrait avoir une compréhension claire des faits, de la situation. Si ce n'est pas le cas, alors vous devez lire la réponse remarquablement complète de paercebal [au fait : merci !].

Ma conclusion pragmatique est d'une simplicité choquante : tous ces trucs de "codage des caractères" du C++ (et de la STL) sont essentiellement cassés et inutiles. Que vous en rendiez responsable Microsoft ou non, cela ne servira à rien de toute façon.

Ma solution, après une enquête approfondie, beaucoup de frustration et les expériences qui en découlent, est la suivante :

  1. accepter que vous soyez responsable de l'encodage et de la conversion (et vous verrez que la plupart de ces opérations sont plutôt triviales).

  2. utiliser std::string pour toute chaîne de caractères codée en UTF-8 (juste une typedef std::string UTF8String )

  3. accepter qu'un tel objet UTF8String n'est qu'un conteneur stupide, mais bon marché. N'accédez jamais directement aux caractères qu'il contient et ne les manipulez jamais (pas de recherche, de remplacement, etc.). Vous pourriez, mais vous n'avez vraiment pas envie de perdre votre temps à écrire des algorithmes de manipulation de texte pour des chaînes de caractères multi-octets ! Même si d'autres personnes ont déjà fait des choses aussi stupides, ne faites pas ça ! Laissez faire ! (Enfin, il y a des scénarios où cela a un sens... utilisez simplement la bibliothèque ICU pour ceux-là).

  4. utiliser std::wstring pour les chaînes encodées en UCS-2 ( typedef std::wstring UCS2String ) - il s'agit d'un compromis, et d'une concession au désordre introduit par l'API WIN32). UCS-2 est suffisant pour la plupart d'entre nous (nous y reviendrons plus tard...).

  5. utiliser les instances UCS2String chaque fois qu'un accès caractère par caractère est nécessaire (lecture, manipulation, etc.). Tout traitement basé sur les caractères doit être effectué dans une représentation NON multi-octet. C'est simple, rapide et facile.

  6. ajout de deux fonctions utilitaires pour convertir en alternance entre UTF-8 et UCS-2 :

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );

Les conversions sont simples, google devrait aider ici ...

C'est ça. Utilisez UTF8String partout où la mémoire est précieuse et pour toutes les E/S UTF-8. Utilisez UCS2String partout où la chaîne doit être analysée et/ou manipulée. Vous pouvez convertir entre ces deux représentations à tout moment.

Alternatives et améliorations

  • Les conversions de & vers des codages de caractères à un octet (par exemple ISO-8859-1) peuvent être réalisées à l'aide de tables de traduction simples, par exemple. const wchar_t tt_iso88951[256] = {0,1,2,...}; et le code approprié pour la conversion vers et depuis UCS2.

  • si le système UCS-2 n'est pas suffisant, passez au système UCS-4 ( typedef std::basic_string<uint32_t> UCS2String )

ICU ou d'autres bibliothèques unicode ?

Pour les trucs avancés.

25voto

  1. Lorsque vous voulez avoir des caractères larges stockés dans votre chaîne. wide dépend de l'implémentation. Visual C++ utilise par défaut le 16 bits si je me souviens bien, tandis que GCC utilise le 16 bits par défaut en fonction de la cible. Ici, la longueur est de 32 bits. Veuillez noter que wchar_t (wide character type) n'a rien à voir avec l'unicode. Il est simplement garanti qu'il peut stocker tous les membres du plus grand jeu de caractères que l'implémentation supporte par ses locales, et au moins aussi long que char. Vous pouvez magasin des chaînes unicodes fines en std::string en utilisant le utf-8 également le codage. Mais il ne comprendra pas la signification des points de code Unicode. Donc str.size() ne vous donnera pas le nombre de caractères logiques dans votre chaîne, mais simplement le nombre d'éléments char ou wchar_t stockés dans cette chaîne/wstring. C'est pour cette raison que les responsables du wrapper C++ de gtk/glib ont développé une méthode de calcul de la taille des caractères. Glib::ustring qui peut gérer l'utf-8.

    Si votre wchar_t a une longueur de 32 bits, alors vous pouvez utiliser utf-32 comme un encodage unicode, et vous pouvez stocker et gère les chaînes unicode en utilisant un encodage fixe (utf-32 est une longueur fixe). Cela signifie que le code de votre wstring s.size() La fonction puis retourne le bon nombre d'éléments wchar_t et caractères logiques.

  2. Oui, un caractère a toujours une longueur d'au moins 8 bits, ce qui signifie qu'il peut stocker toutes les valeurs ASCII.

  3. Oui, tous les principaux compilateurs le supportent.

6voto

J'utilise fréquemment std::string pour contenir les caractères utf-8 sans aucun problème. Je recommande vivement de le faire lors de l'interfaçage avec des API qui utilisent utf-8 comme type de chaîne natif.

Par exemple, j'utilise utf-8 lorsque j'interface mon code avec l'interpréteur Tcl.

L'inconvénient majeur est que la longueur de la chaîne std::string, n'est plus le nombre de caractères de la chaîne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X