Question std :: wstring VS std :: chaîne


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

  1. Quand devrais-je utiliser std::wstring plus de std::string?
  2. Pouvez std::string contenir l'ensemble du jeu de caractères ASCII, y compris les caractères spéciaux?
  3. Est std::wstring supporté par tous les compilateurs C ++ populaires?
  4. Qu'est-ce qu'un "caractère large"?

643
2017-12-31 04:08


origine


Réponses:


string? wstring?

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

char contre. wchar_t

char est censé contenir un personnage, généralement un caractère de 1 octet. wchar_t est censé contenir un personnage large, et puis, les choses deviennent difficiles: Sur Linux, un wchar_t est de 4 octets, alors que sur Windows, c'est 2 octets

qu'en est-il de Unicode, puis?

Le problème est que ni char ni wchar_t est directement lié à unicode.

Sur Linux?

Prenons un OS Linux: Mon système Ubuntu est déjà unicode. Quand je travaille avec une chaîne de caractères, elle est encodée nativement 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é" ;


   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(bytes)     :" ;

   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 ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   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(bytes)    :" ;

   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;
}

produit le texte suivant:

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

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

Vous verrez le texte "olé" dans char est vraiment construit par quatre caractères: 110, 108, 195 et 169 (sans compter le zéro final). (Je vais vous laisser étudier le wchar_t code comme un exercice)

Ainsi, lorsque vous travaillez avec un char sous Linux, vous devriez généralement utiliser Unicode sans même le savoir. Et comme std :: string fonctionne avec char, alors std :: string est déjà prêt pour unicode.

Notez que std :: string, comme l'API de la chaîne C, considèrera la chaîne "olé" comme ayant 4 caractères, pas trois. Donc, vous devriez être prudent en tronquant / jouant avec des caractères Unicode car une combinaison de caractères est interdite en UTF-8.

Sur Windows?

Sur Windows, c'est un peu différent. Win32 a dû supporter beaucoup d'application en travaillant avec char et sur différents charsets/pages de codes produit dans le monde entier, avant l'avènement de l'Unicode.

Leur solution était donc intéressante: si une application fonctionne avec char, puis les chaînes de caractères sont codées / imprimées / affichées sur les étiquettes GUI en utilisant le jeu de caractères local / page de codes sur la machine. Par exemple, "olé" serait "olé" dans un Windows localisé en français, mais serait quelque chose de différent sur un Windows localisé en cyrillique ("olé" si vous utilisez Windows-1251). Ainsi, les "applications historiques" fonctionneront toujours de la même manière.

Pour les applications basées sur Unicode, Windows utilise wchar_t, qui fait 2 octets de large et est codé UTF-16, qui est codé Unicode sur des caractères de 2 octets (ou à tout le moins, l'UCS-2 le plus compatible, ce qui est presque la même chose IIRC).

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

Ainsi, si vous travaillez sur Windows, vous veut vraiment utiliser wchar_t (sauf si vous utilisez un cadre qui cache, comme GTK + ou QT...). Le fait est que dans les coulisses, Windows travaille avec wchar_t chaînes, de sorte que même les applications historiques auront leur char cordes converties en wchar_t lors de l'utilisation de l'API comme SetWindowText (fonction API de bas niveau pour définir l'étiquette sur une interface graphique Win32).

Problèmes de mémoire?

UTF-32 est de 4 octets par caractères, donc il n'y a pas grand chose à ajouter, si seulement un texte UTF-8 et UTF-16 utilisent 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, alors vous devriez savoir que pour la plupart des langues occidentales, le texte UTF-8 utilisera moins de mémoire que le même UTF-16.

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

Dans l'ensemble, UTF-16 utilisera principalement 2 octets par caractères (sauf si vous avez affaire à un type de glyphes de langue ésotérique (Klingon? Elfique?), Tandis que UTF-8 passera de 1 à 4 octets.

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

Conclusion

1. Quand devrais-je utiliser std :: wstring sur std :: string?

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

(§): sauf si vous utilisez une boîte à outils / cadre indiquant autrement

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

Remarque: Une chaîne std :: string est appropriée pour contenir un tampon 'binaire', alors que std :: wstring ne l'est pas!

Sur Linux? Oui.
Sur Windows? Seuls les caractères spéciaux sont disponibles pour les paramètres régionaux actuels de l'utilisateur Windows.

Edit (Après un commentaire de Johann Gerell): une chaîne std :: suffira pour gérer toutes les chaînes à base de caractères (chaque caractère étant un nombre compris entre 0 et 255). Mais:

  1. ASCII est supposé aller de 0 à 127. Les caractères plus élevés ne sont PAS ASCII.
  2. un caractère de 0 à 127 sera tenu correctement
  3. un char de 128 à 255 aura une signification dépendant de votre encodage (unicode, non-unicode, etc.), mais il pourra contenir tous les glyphes Unicode tant qu'ils sont codés en UTF-8.

3. Est-ce que std :: wstring est supporté par presque tous les compilateurs C ++ populaires?

Surtout, à l'exception des compilateurs basés sur GCC qui sont portés sur Windows
Il 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?

Sur C / C ++, c'est un type de caractère écrit wchar_t qui est plus grand que le simple char type de caractère. Il est supposé être utilisé pour mettre à l'intérieur des caractères dont les index (comme les glyphes Unicode) sont plus grands que 255 (ou 127, selon ...)


884
2017-12-31 12:47



Je recommande d'éviter std::wstring sous Windows ou ailleurs, sauf si cela est requis par l'interface, ou à proximité d'appels d'API Windows et de conversions d'encodage respectives en tant que sucre syntaxique.

Mon point de vue est résumé dans http://utf8everywhere.org dont je suis un co-auteur.

Sauf si votre application est centrée sur l'API, par ex. principalement l'application UI, la suggestion est de stocker les chaînes Unicode dans std :: string et codé en UTF-8, effectuant la conversion à proximité des appels d'API. Les avantages décrits dans l'article l'emportent sur l'ennui apparent de la conversion, en particulier dans les applications complexes. C'est doublement le cas pour le développement multi-plateforme et bibliothèque.

Et maintenant, répondant à vos questions:

  1. Quelques raisons faibles. Il existe pour des raisons historiques, où les widechars étaient considérés comme la bonne façon de soutenir Unicode. Il est maintenant utilisé pour interfacer les API qui préfèrent les chaînes UTF-16. Je les utilise uniquement à proximité immédiate de ces appels API.
  2. Cela n'a rien à voir avec std :: string. Il peut contenir n'importe quel encodage que vous y mettez. La seule question est comment Toi Traitez son contenu. Ma recommandation est UTF-8, donc il sera capable de contenir tous les caractères Unicode correctement. C'est une pratique courante sur Linux, mais je pense que les programmes Windows devraient le faire aussi.
  3. Non.
  4. Le caractère large est un nom confus. Dans les premiers jours d'Unicode, il y avait une croyance que le caractère peut être codé dans deux octets, d'où le nom. Aujourd'hui, il signifie «n'importe quelle partie du personnage qui a deux octets de long». UTF-16 est vu comme une séquence de telles paires d'octets (aka caractères larges). Un caractère en UTF-16 prend une ou deux paires.

47
2017-12-29 16:14



Ainsi, chaque lecteur ici maintenant devrait avoir une compréhension claire des faits, de la situation. Si non, alors vous devez lire la réponse exceptionnellement complète de paercebal [btw: merci!].

Ma conclusion pragmatique est choquante simple: tout ce que C ++ (et STL) "l'encodage de caractères" est sensiblement cassé et inutile. Blâmez-le sur Microsoft ou pas, cela ne va pas aider 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 devez être responsable de l'encodage et de la conversion (et vous verrez que cela est plutôt trivial)

  2. utilisez std :: string pour toutes les chaînes codées en UTF-8 (juste un typedef std::string UTF8String)

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

  4. utilisez std :: wstring pour les chaînes codées en UCS-2 (typedef std::wstring UCS2String) - c'est un compromis, et une concession au désordre que l'API WIN32 a introduit). UCS-2 est suffisant pour la plupart d'entre nous (plus sur cela plus tard ...).

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

  6. ajoutez deux fonctions d'utilité pour convertir en avant et en arrière 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 tout. 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 depuis et vers des codages de caractères à octet unique (par exemple ISO-8859-1) peuvent être réalisées à l'aide de tables de traduction simples, par ex. const wchar_t tt_iso88951[256] = {0,1,2,...}; et le code approprié pour la conversion de et vers UCS2.

  • si UCS-2 n'est pas suffisant, que de passer à UCS-4 (typedef std::basic_string<uint32_t> UCS2String)

ICU ou d'autres bibliothèques Unicode?

Pour des trucs avancés.


35
2017-11-07 06:07



  1. Lorsque vous voulez avoir des caractères larges stockés dans votre chaîne. wide dépend de la mise en œuvre. Visual C ++ par défaut à 16 bits si je me souviens bien, tandis que GCC par défaut en fonction de la cible. C'est 32 bits ici. Veuillez noter que wchar_t (type de caractère large) n'a rien à voir avec 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 longtemps que char. Vous pouvez le magasin cordes unicode bien dans std::string en utilisant le utf-8 encodage aussi. Mais il ne comprendra pas la signification des points de code Unicode. Alors str.size() ne vous donnera pas la quantité de caractères logiques dans votre chaîne, mais simplement la quantité d'éléments char ou wchar_t stockés dans cette chaîne / wstring. Pour cette raison, les gens de l'encapsuleur C ++ gtk / glib ont développé un Glib::ustring classe qui peut gérer utf-8.

    Si votre wchar_t est long de 32 bits, alors vous pouvez utiliser utf-32 comme un encodage Unicode, et vous pouvez stocker et gérer les chaînes Unicode en utilisant un encodage fixe (utf-32 est une longueur fixe). Cela signifie que votre wstring s.size() fonction sera puis renvoie la bonne quantité d'éléments wchar_t et caractères logiques.

  2. Oui, char 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.

23
2017-12-31 11:48



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

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

La mise en garde majeure est la longueur de la chaîne std ::, n'est plus le nombre de caractères dans la chaîne.


5
2017-12-31 04:33



  1. Lorsque vous voulez stocker des caractères 'wide' (Unicode).
  2. Oui: 255 d'entre eux (sauf 0).
  3. Oui.
  4. Voici un article d'introduction: http://www.joelonsoftware.com/articles/Unicode.html

3
2017-12-31 04:16



Les applications qui ne sont pas satisfaites de seulement 256 caractères différents ont l'option d'utiliser des caractères larges (plus de 8 bits) ou un codage de longueur variable (un codage multi-octets en terminologie C ++) tel que UTF-8. Les caractères larges nécessitent généralement plus d'espace qu'un codage de longueur variable, mais ils sont plus rapides à traiter. Les applications multilingues qui traitent de grandes quantités de texte utilisent généralement des caractères larges lors du traitement du texte, mais le convertissent en UTF-8 lors de son stockage sur disque.

La seule différence entre un string et un wstring est le type de données des caractères qu'ils stockent. Une chaîne de magasins chars dont la taille est garantie d'au moins 8 bits, vous pouvez donc utiliser des chaînes pour le traitement, par ex. Texte ASCII, ISO-8859-15 ou UTF-8. La norme ne dit rien sur le jeu de caractères ou l'encodage.

Pratiquement chaque compilateur utilise un jeu de caractères dont les 128 premiers caractères correspondent à ASCII. C'est également le cas avec les compilateurs qui utilisent le codage UTF-8. La chose importante à savoir lors de l'utilisation de chaînes en UTF-8 ou d'un autre encodage de longueur variable est que les indices et les longueurs sont mesurés en octets, pas en caractères.

Le type de données d'un wstring est wchar_t, dont la taille n'est pas définie dans la norme, sauf qu'elle doit être au moins aussi grande qu'un caractère, généralement 16 bits ou 32 bits. wstring peut être utilisé pour traiter du texte dans l'encodage à caractères larges défini par l'implémentation. Comme le codage n'est pas défini dans la norme, il n'est pas simple de convertir les chaînes et les chaînes. On ne peut pas supposer que wstrings ait un encodage de longueur fixe non plus.

Si vous n'avez pas besoin d'un support multilingue, vous pouvez utiliser uniquement des chaînes normales. D'un autre côté, si vous écrivez une application graphique, il arrive souvent que l'API ne prenne en charge que des caractères larges. Ensuite, vous voudrez probablement utiliser les mêmes caractères larges lors du traitement du texte. Gardez à l'esprit que UTF-16 est un encodage de longueur variable, ce qui signifie que vous ne pouvez pas supposer length() pour retourner le nombre de caractères. Si l'API utilise un codage à longueur fixe, tel que UCS-2, le traitement devient facile. La conversion entre caractères larges et UTF-8 est difficile à faire de manière portable, mais là encore, votre API d'interface utilisateur prend probablement en charge la conversion.


2
2017-09-11 09:28



  1. quand vous voulez utiliser des chaînes Unicode et pas seulement des ascii, utile pour l'internationalisation
  2. oui, mais ça ne joue pas bien avec 0
  3. pas au courant de tout ce qui ne le fait pas
  4. caractère large est la manière spécifique au compilateur de gérer la représentation de longueur fixe d'un caractère unicode, pour MSVC c'est un caractère de 2 octets, pour gcc je comprends c'est 4 octets. et +1 pour http://www.joelonsoftware.com/articles/Unicode.html

1
2017-12-31 04:16



1) Comme mentionné par Greg, wstring est utile pour l'internationalisation, c'est à ce moment que vous libérerez votre produit dans d'autres langues que l'anglais

4) Vérifiez ceci pour le caractère large http://en.wikipedia.org/wiki/Wide_character


1
2017-12-31 04:24