std :: wstring VS std :: string

742

Je ne peux pas comprendre les différences entre std::stringet std::wstring. Je sais que wstringprend en charge les caractères larges tels que les caractères Unicode. J'ai les questions suivantes:

  1. Quand devrais-je utiliser std::wstringplus std::string?
  2. Peut std::stringcontenir l'ensemble du jeu de caractères ASCII, y compris les caractères spéciaux?
  3. Est std::wstringpris en charge par tous les compilateurs C ++ populaires?
  4. Qu'est-ce qu'un " caractère large " exactement ?
Rapptz
la source
10
Le jeu de caractères ASCII n'a pas beaucoup de caractères "spéciaux", le plus exotique est probablement `(backquote). std :: string peut contenir environ 0,025% de tous les caractères Unicode (généralement, 8 bits)
MSalters
3
De bonnes informations sur les caractères larges et le type à utiliser peuvent être trouvées ici: programmers.stackexchange.com/questions/102205/…
Yariv
14
Eh bien, et puisque nous sommes en 2012, utf8everywhere.org a été écrit. Il répond à peu près à toutes les questions sur les droits et les torts avec C ++ / Windows.
Pavel Radzivilovsky
42
@MSalters: std :: string peut contenir 100% de tous les caractères Unicode, même si CHAR_BIT est 8. Cela dépend du codage de std :: string, qui peut être UTF-8 au niveau du système (comme presque partout sauf pour Windows ) ou au niveau de votre application. L'encodage étroit natif ne prend pas en charge Unicode? Pas de problème, ne l'utilisez pas, utilisez plutôt UTF-8.
Yakov Galka
8
Grande lecture sur ce sujet: utf8everywhere.org
Timothy Shields

Réponses:

993

string? wstring?

std::stringest un basic_stringmodèle sur un char, et std::wstringsur un wchar_t.

char contre. wchar_t

charest censé contenir un caractère, généralement un caractère 8 bits.
wchar_test censé contenir un caractère large, puis les choses se compliquent:
sous Linux, a wchar_tvaut 4 octets, tandis que sous Windows, c'est 2 octets.

Et Unicode alors?

Le problème est que ni l'un charni l'autre wchar_tn'est directement lié à l'unicode.

Sous Linux?

Prenons un système d'exploitation Linux: mon système Ubuntu est déjà compatible avec l'Unicode. Lorsque je travaille avec une chaîne de caractères, elle est encodée en natif en 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(ordinals)  :" ;

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

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

affiche le texte suivant:

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

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

Vous verrez que le texte "olé" dans charest vraiment construit par quatre caractères: 110, 108, 195 et 169 (sans compter le zéro de fin). (Je vous laisse étudier le wchar_tcode comme un exercice)

Donc, lorsque vous travaillez avec un charsous Linux, vous devriez généralement utiliser Unicode sans même le savoir. Et comme std::stringfonctionne avec char, il std::stringest donc déjà prêt pour Unicode.

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

Sous Windows?

Sous Windows, c'est un peu différent. Win32 devait prendre en charge de nombreuses applications fonctionnant avec charet sur différents jeux de caractères / pages de codes produites dans le monde entier, avant l'avènement d'Unicode.

Leur solution était donc intéressante: si une application fonctionne avec char, alors les chaînes de caractères sont encodées / imprimées / affichées sur les étiquettes GUI en utilisant le jeu de caractères / page de code local 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é cyrillique ("olé" si vous utilisez 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 est large de 2 octets, et est codé en UTF-16 , qui est codé en Unicode sur des caractères de 2 octets (ou à tout le moins, le UCS-2 le plus compatible, qui est presque le même chose IIRC).

Les applications utilisant charsont dites "multi-octets" (car chaque glyphe est composé d'un ou plusieurs chars), tandis que les applications utilisant wchar_tsont dites "widechar" (car chaque glyphe est composé d'un ou deux wchar_t. Voir MultiByteToWideChar et WideCharToMultiByte Win32 API de conversion pour plus d'informations.

Ainsi, si vous travaillez sous Windows, vous souhaitez absolument l' utiliser wchar_t(sauf si vous utilisez un framework qui le cache, comme GTK + ou QT ...). Le fait est que dans les coulisses, Windows fonctionne avec des wchar_tchaînes, donc même les applications historiques auront leurs charchaînes converties wchar_tlors 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ère, donc il n'y a pas grand-chose à ajouter, si seulement qu'un texte UTF-8 et 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, vous devez savoir que pour la plupart des langues occidentales, le texte UTF-8 utilisera moins de mémoire que le même UTF-16.

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

Dans l'ensemble, UTF-16 utilisera principalement 2 et parfois 4 octets par caractère (à moins que vous n'ayez affaire à une sorte de glyphe de langage ésotérique (Klingon? Elfique?), Tandis que 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 sur std :: string?

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

    (§): sauf indication contraire

  2. Peut std::stringcontenir tous les jeux de caractères ASCII, y compris les caractères spéciaux?

    Remarque: A std::stringconvient pour contenir un tampon «binaire», où a std::wstringne l'est pas!

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

    Edit (après un commentaire de Johann Gerell ):
    un std::stringsuffira pour gérer toutes les charchaînes de base (chacune charétant un nombre de 0 à 255). Mais:

    1. ASCII est censé aller de 0 à 127. Les chars supérieurs ne sont PAS ASCII.
    2. un charde 0 à 127 sera tenu correctement
    3. un charde 128 à 255 aura une signification en fonction 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. Est std::wstringpris en charge par presque tous les compilateurs C ++ populaires?

    Généralement, à 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 exactement?

    Sur C / C ++, c'est un type de caractère écrit wchar_tqui est plus grand que le chartype de caractère simple . 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 ...).

paercebal
la source
4
@gnud: Peut-être que wchar_t était censé être suffisant pour gérer tous les caractères UCS-2 (la plupart des caractères UTF-16) avant l'avènement de l'UTF-16 ... Ou peut-être que Microsoft avait d'autres priorités que POSIX, comme donner un accès facile à Unicode sans modifier l'utilisation codée de char sur Win32.
paercebal
4
@ Sorin Sbarnea: UTF-8 pourrait prendre 1-6 octets, mais apparemment la norme le limite à 1-4. Voir en.wikipedia.org/wiki/UTF8#Description pour plus d'informations.
paercebal
8
Bien que ces exemples produisent des résultats différents sous Linux et Windows, le programme C ++ contient un comportement défini par l'implémentation pour savoir s'il olèest codé en UTF-8 ou non. De plus, la raison pour laquelle vous ne pouvez pas nativement diffuser wchar_t *à std::coutest parce que les types sont incompatibles résultant dans un programme mal formé et il n'a rien à voir avec l'utilisation de codages. Il convient de souligner que si vous utilisez std::stringou std::wstringdépend de vos propres préférences d'encodage plutôt que de la plate-forme, surtout si vous voulez que votre code soit portable.
John Leidegren
14
Windows utilise en fait UTF-16 et depuis un certain temps, les anciennes versions de Windows utilisaient UCS-2 mais ce n'est plus le cas. Mon seul problème ici est la conclusion qui std::wstringdevrait être utilisée sous Windows car elle correspond mieux à l'API Windows Unicode qui, je pense, est fallacieuse. Si votre seule préoccupation était d'appeler l'API Windows Unicode et de ne pas trier les chaînes, bien sûr, mais je n'achète pas cela comme cas général.
John Leidegren
15
@ John Leidegren:: If your only concern was calling into the Unicode Windows API and not marshalling strings then sureAlors, nous sommes d'accord. Je code en C ++, pas en JavaScript. Éviter le marshalling inutile ou tout autre traitement potentiellement coûteux au moment de l'exécution alors qu'il peut être effectué au moment de la compilation est au cœur de ce langage. Le codage contre WinAPI et son utilisation ne std::stringsont qu'un gaspillage injustifié des ressources d'exécution. Vous le trouvez fallacieux, et c'est OK, car c'est votre point de vue. Le mien est que je n'écrirai pas de code avec pessimisation sous Windows simplement parce qu'il semble mieux du côté Linux.
paercebal
71

Je recommande d'éviter std::wstringsur Windows ou ailleurs, sauf lorsque requis par l'interface, ou n'importe où près des appels d'API Windows et des conversions d'encodage respectives comme sucre syntaxique.

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

À moins que votre application ne soit centrée sur l'API, par exemple principalement une application d'interface utilisateur, la suggestion est de stocker les chaînes Unicode dans std :: string et encodées en UTF-8, en effectuant une conversion près des appels d'API. Les avantages décrits dans l'article l'emportent sur la gêne apparente 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 le bon moyen de prendre en charge Unicode. Il est désormais utilisé pour interfacer les API qui préfèrent les chaînes UTF-16. Je les utilise uniquement à proximité directe de ces appels API.
  2. Cela n'a rien à voir avec std :: string. Il peut contenir tout l'encodage que vous y mettez. La seule question est de savoir comment vous traitez son contenu. Ma recommandation est UTF-8, donc il pourra contenir correctement tous les caractères Unicode. C'est une pratique courante sous Linux, mais je pense que les programmes Windows devraient le faire aussi.
  3. Non.
  4. Le caractère large est un nom déroutant. Dans les premiers jours d'Unicode, on croyait qu'un caractère peut être codé sur deux octets, d'où le nom. Aujourd'hui, il représente "n'importe quelle partie du caractère de deux octets". UTF-16 est considéré comme une séquence de telles paires d'octets (également appelées caractères larges). Un personnage en UTF-16 prend une ou deux paires.
Pavel Radzivilovsky
la source
37

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

Ma conclusion pragmatique est d'une simplicité choquante: tout ce qui est "l'encodage de caractères" en C ++ (et STL) est substantiellement cassé et inutile. Blâmez-le sur Microsoft ou non, cela n'aidera pas de toute façon.

Ma solution, après une enquête approfondie, beaucoup de frustration et les expériences consécutives sont les suivantes:

  1. Acceptez, que vous devez être responsable de vous-même pour l'encodage et la conversion (et vous verrez qu'une grande partie est plutôt triviale)

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

  3. accepter qu'un tel objet UTF8String est juste un conteneur stupide mais bon marché. N'y accédez jamais et / ou ne manipulez jamais directement des caractères (pas de recherche, remplacement, etc.). Vous pourriez, mais vous ne voulez vraiment pas vraiment, vraiment perdre votre temps à écrire des algorithmes de manipulation de texte pour des 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-ci).

  4. utilisez std :: wstring pour les chaînes encodées UCS-2 ( typedef std::wstring UCS2String) - c'est un compromis et une concession au bordel que l'API WIN32 a introduit). UCS-2 est suffisant pour la plupart d'entre nous (plus à ce sujet plus tard ...).

  5. utiliser 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 utilitaires pour convertir en va-et-vient entre UTF-8 et UCS-2:

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

Les conversions sont simples, Google devrait vous 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 à tout moment entre ces deux représentations.

Alternatives et améliorations

  • les conversions de et vers les 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 du code approprié pour la conversion vers et depuis UCS2.

  • si UCS-2 ne suffit pas, passez à UCS-4 ( typedef std::basic_string<uint32_t> UCS2String)

ICU ou autres bibliothèques Unicode?

Pour des trucs avancés.

Frunsi
la source
Dang, ce n'est pas bon de savoir que le support natif d'Unicode n'est pas là.
Mihai Danila
@Frunsi, je suis curieux de savoir si vous avez essayé Glib :: ustring et si oui, qu'en pensez-vous?
Caroline Beltran
@CarolineBeltran: Je connais Glib, mais je ne l'ai jamais utilisé, et je ne l'utiliserai probablement jamais, car il est plutôt limité à une plate-forme cible assez peu spécifique (systèmes unixoïdes ...). Son port Windows est basé sur une couche externe win2unix, et à mon humble avis, il n'y a aucune couche de compatibilité OSX. Tout cela oriente clairement dans une mauvaise direction, au moins pour mon code (à ce niveau de l'arche ...) ;-) Donc, Glib n'est pas une option
Frunsi
9
La recherche, le remplacement et ainsi de suite fonctionnent très bien sur les chaînes UTF-8 (une partie de la séquence d'octets représentant un caractère ne peut jamais être mal interprétée comme un autre caractère). En fait, UTF-16 et UTF-32 ne facilitent pas du tout cela: les trois encodages sont en fait des encodages multi-octets, car un caractère perçu par l'utilisateur (grappe de graphèmes) peut avoir un nombre illimité de points de code unicode! La solution pragmatique consiste à utiliser UTF-8 pour tout, et à convertir en UTF-16 uniquement lorsque vous traitez avec l'API Windows.
Daniel
5
@Frunsi: La recherche et le remplacement fonctionnent aussi bien avec UTF-8 qu'avec UTF-32. C'est précisément parce que le traitement de texte Unicode approprié doit de toute façon gérer les `` caractères '' multi-points de code, que l'utilisation d'un codage de longueur variable comme UTF-8 ne rend pas le traitement des chaînes plus compliqué. Il suffit donc d'utiliser UTF-8 partout. Les fonctions de chaîne C normales fonctionneront bien sur UTF-8 (et correspondent aux comparaisons ordinales sur la chaîne Unicode), et si vous avez besoin de quelque chose de plus sensible au langage, vous devrez quand même appeler une bibliothèque Unicode, UTF-16/32 ne peut pas vous sauver de cela.
Daniel
25
  1. Lorsque vous souhaitez stocker des caractères larges dans votre chaîne. widedépend de la mise en œuvre. Visual C ++ par défaut à 16 bits si je me souviens bien, tandis que GCC par défaut dépend de la cible. C'est 32 bits de long ici. Veuillez noter que wchar_t (type de caractère large) 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 pris en charge par l'implémentation par ses paramètres régionaux, et au moins aussi longtemps que char. Vous pouvez également stocker correctement les chaînes unicode en std::stringutilisant l' utf-8encodage. Mais il ne comprendra pas la signification des points de code unicode. Doncstr.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 / chaîne de caractères. Pour cette raison, les gens du wrapper gtk / glib C ++ ont développé une Glib::ustringclasse qui peut gérer utf-8.

    Si votre wchar_t a une longueur de 32 bits, vous pouvez l'utiliser utf-32comme encodage unicode et vous pouvez stocker et gérer des chaînes unicode à l'aide d'un codage fixe (utf-32 est de longueur fixe). Cela signifie que la s.size()fonction de votre wstring retournera alors la bonne quantité d'éléments wchar_t et de caractères logiques.

  2. Oui, char est toujours d'au moins 8 bits, ce qui signifie qu'il peut stocker toutes les valeurs ASCII.
  3. Oui, tous les grands compilateurs le prennent en charge.
Johannes Schaub - litb
la source
Je suis curieux de savoir # 2. Je pensais que 7 bits seraient techniquement valables aussi? Ou est-il nécessaire de pouvoir stocker quoi que ce soit après les caractères ASCII 7 bits?
jalf
1
oui, jalf. c89 spécifie des plages minimales pour les types de base dans sa documentation de limits.h (pour les caractères non signés, c'est 0..255 min), et un système binaire pur pour les types entiers. il suit char, unsigned char et signé char ont des longueurs de bits minimum de 8. c ++ hérite de ces règles.
Johannes Schaub - litb
15
"Cela signifie que la fonction s.size () de votre wstring retournera alors la bonne quantité d'éléments wchar_t et de caractères logiques." Ce n'est pas tout à fait exact, même pour Unicode. Il serait plus précis de dire point de code que "caractère logique", même en UTF-32, un caractère donné peut être composé de plusieurs points de code.
Logan Capaldo
Êtes-vous essentiellement en train de dire que C ++ n'a pas de support natif pour le jeu de caractères Unicode?
Mihai Danila
1
"Mais il ne comprendra pas la signification des points de code Unicode." Sur les fenêtres, non plus std::wstring.
Déduplicateur
5

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 également utf-8 comme type de chaîne natif.

Par exemple, j'utilise utf-8 lors de l'interface de 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.


la source
1
Juan: Voulez-vous dire que std :: string peut contenir tous les caractères unicode mais que la longueur ne sera pas indiquée correctement? Y a-t-il une raison pour laquelle il rapporte une longueur incorrecte?
3
Lors de l'utilisation du codage utf-8, un seul caractère unicode peut être composé de plusieurs octets. C'est pourquoi l'encodage utf-8 est plus petit lorsque vous utilisez principalement des caractères de l'ensemble ascii standard. Vous devez utiliser des fonctions spéciales (ou lancer les vôtres) pour mesurer le nombre de caractères unicode.
2
(Spécifique à Windows) La plupart des fonctions s'attendent à ce qu'une chaîne utilisant des octets soit ASCII et 2 octets soit Unicode, les anciennes versions MBCS. Ce qui signifie que si vous stockez unicode 8 bits que vous devrez convertir en unicode 16 bits pour appeler une fonction Windows standard (sauf si vous n'utilisez que la partie ASCII).
Greg Domjan
2
Non seulement une chaîne std :: reportera la longueur de manière incorrecte, mais elle affichera également la mauvaise chaîne. Si un caractère Unicode est représenté dans UTF-8 comme plusieurs octets, ce que std :: string considère comme ses propres caractères, alors vos routines de manipulation typiquement std :: string sortiront probablement les plusieurs caractères étranges qui résultent de la mauvaise interprétation de celui-ci. caractère correct.
Mihai Danila
2
Je suggère de changer la réponse pour indiquer que les chaînes doivent être considérées uniquement comme des conteneurs d'octets, et, si les octets sont un codage Unicode (UTF-8, UTF-16, ...), alors vous devez utiliser des bibliothèques spécifiques qui comprennent cette. Les API standard basées sur des chaînes (longueur, substr, etc.) échoueront lamentablement avec des caractères multi-octets. Si cette mise à jour est effectuée, je supprimerai mon downvote.
Mihai Danila
4
  1. Lorsque vous souhaitez stocker des caractères "larges" (Unicode).
  2. Oui: 255 d'entre eux (hors 0).
  3. Oui.
  4. Voici un article d'introduction: http://www.joelonsoftware.com/articles/Unicode.html
ChrisW
la source
11
std :: string peut contenir 0 très bien (soyez prudent si vous appelez la méthode c_str ())
Mr Fooz
3
Et à proprement parler, un caractère n'est pas garanti à 8 bits. :) Votre lien dans # 4 est une lecture incontournable, mais je ne pense pas qu'il réponde à la question. Un caractère large n'a strictement rien à voir avec l'unicode. C'est simplement un personnage plus large. (La
largeur
2
  1. lorsque vous souhaitez utiliser des chaînes Unicode et pas seulement ascii, utile pour l'internationalisation
  2. oui, mais ça ne fonctionne pas bien avec 0
  3. pas au courant de ceux qui ne le font pas
  4. le 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, il s'agit d'un caractère à 2 octets, pour gcc, je comprends qu'il est de 4 octets. et un +1 pour http://www.joelonsoftware.com/articles/Unicode.html
Greg Domjan
la source
1
2. Une chaîne std :: peut très bien contenir un caractère NULL. Il peut également contenir des caractères utf-8 et larges.
@Juan: Cela m'a de nouveau mis dans la confusion. Si std :: string peut conserver des caractères unicode, quelle est la particularité de std :: wstring?
1
@Appu: std :: string peut contenir des caractères Unicode UTF-8. Il existe un certain nombre de normes Unicode ciblées sur différentes largeurs de caractères. UTf8 a une largeur de 8 bits. Il y a aussi UTF-16 et UTF-32 à 16 et 32 ​​bits de large respectivement
Greg D
Avec un std :: wstring. Chaque caractère unicode peut être un wchar_t lors de l'utilisation des encodages de longueur fixe. Par exemple, si vous choisissez d'utiliser l'approche joel on software en tant que lien Greg. La longueur de la chaîne wstring correspond alors exactement au nombre de caractères unicode de la chaîne. Mais cela prend plus de place
Je n'ai pas dit qu'il ne pouvait pas contenir un 0 '\ 0', et ce que je voulais dire par ne fonctionne pas bien, c'est que certaines méthodes peuvent ne pas vous donner un résultat attendu contenant toutes les données de la chaîne. Si dur sur les votes négatifs.
Greg Domjan
2

Les applications qui ne sont pas satisfaites avec seulement 256 caractères différents ont la possibilité d'utiliser des caractères larges (plus de 8 bits) ou un codage de longueur variable (un codage multi-octets dans la terminologie C ++) tel que UTF-8. Les caractères larges nécessitent généralement plus d'espace qu'un encodage de longueur variable, mais 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 du stockage sur disque.

La seule différence entre a stringet a wstringest le type de données des caractères qu'ils stockent. Une chaîne stocke des chars dont la taille est garantie d'être d'au moins 8 bits, vous pouvez donc utiliser des chaînes pour traiter par exemple du texte ASCII, ISO-8859-15 ou UTF-8. La norme ne dit rien sur le jeu de caractères ou l'encodage.

Pratiquement tous les compilateurs utilisent 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 lorsque vous utilisez des chaînes en UTF-8 ou un autre codage de longueur variable, c'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 le codage à caractères larges défini par l'implémentation. Étant donné que l'encodage n'est pas défini dans la norme, il n'est pas simple de convertir entre chaînes et wstrings. On ne peut pas non plus supposer que wstrings a un codage de longueur fixe.

Si vous n'avez pas besoin de prise en charge multilingue, vous pourriez être en mesure d'utiliser uniquement des chaînes régulières. En revanche, si vous écrivez une application graphique, il arrive souvent que l'API ne prenne en charge que les caractères larges. Ensuite, vous souhaiterez 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()renvoyer le nombre de caractères. Si l'API utilise un encodage de longueur fixe, tel que UCS-2, le traitement devient facile. La conversion entre des caractères larges et UTF-8 est difficile à faire de manière portable, mais là encore, votre interface utilisateur API prend probablement en charge la conversion.

Seppo Enarvi
la source
Donc, en paraphrasant le premier paragraphe: Une application nécessitant plus de 256 caractères doit utiliser un codage multi-octets ou peut-être un codage multi-octets.
Déduplicateur
En règle générale, les codages 16 et 32 ​​bits tels que UCS-2 et UCS-4 ne sont pas appelés codages multioctets. La norme C ++ fait la distinction entre les codages multi-octets et les caractères larges. Une représentation de caractères larges utilise un nombre fixe (généralement plus de 8) bits par caractère. Les codages qui utilisent un seul octet pour coder les caractères les plus courants et plusieurs octets pour coder le reste du jeu de caractères sont appelés codages multioctets.
Seppo Enarvi
Désolé, commentaire bâclé. Devrait avoir dit codage de longueur variable. UTF-16 est un codage de longueur variable, tout comme UTF-8. Prétendre que ce n'est pas le cas est une mauvaise idée.
Déduplicateur
C'est un bon point. Il n'y a aucune raison pour que wstrings ne puisse pas être utilisé pour stocker UTF-16 (au lieu de UCS-2), mais la commodité d'un encodage de longueur fixe est perdue.
Seppo Enarvi du
2

Une bonne question! Je pense que le CODAGE DES DONNÉES (parfois un CHARSET est également impliqué) est une EXPRESSION DE MÉMOIRE MÉCANISME D' DE afin d'enregistrer des données dans un fichier ou de transférer des données via un réseau, donc je réponds à cette question comme suit:

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

Si la plate-forme de programmation ou la fonction API est un octet unique, et que nous voulons traiter ou analyser certaines données Unicode, par exemple, lire à partir du fichier Windows'.REG ou du flux réseau de 2 octets, nous devons déclarer la variable std :: wstring facilement les traiter. par exemple: wstring ws = L "中国 a" (mémoire de 6 octets: 0x4E2D 0x56FD 0x0061), nous pouvons utiliser ws [0] pour obtenir le caractère '中' et ws [1] pour obtenir le caractère '国' et ws [2] pour obtenir le caractère «a», etc.

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

Oui. Mais remarquez: ASCII américain, signifie que chaque octet 0x00 ~ 0xFF représente un caractère, y compris le texte imprimable tel que "123abc & * _ &" et vous en avez dit un spécial, l'imprimant principalement sous la forme d'un '.' éviter de confondre les éditeurs ou les terminaux. Et certains autres pays étendent leur propre jeu de caractères "ASCII", par exemple le chinois, utilise 2 octets pour représenter un caractère.

3. std :: wstring est-il pris en charge par tous les compilateurs C ++ populaires?

Peut-être, ou surtout. J'ai utilisé: VC ++ 6 et GCC 3.3, YES

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

un caractère large indique principalement l'utilisation de 2 octets ou 4 octets pour contenir tous les caractères des pays. 2 octets UCS2 est un échantillon représentatif, et en outre par exemple en anglais 'a', sa mémoire est de 2 octets de 0x0061 (vs en ASCII 'la mémoire de a est de 1 octet 0x61)

Leiyi.China
la source
0

Il y a de très bonnes réponses ici, mais je pense qu'il y a quelques choses que je peux ajouter concernant Windows / Visual Studio. C'est basé sur mon expérience avec VS2015. Sous Linux, la solution consiste essentiellement à utiliser UTF-8 encodé std::stringpartout. Sous Windows / VS, cela devient plus complexe. Voici pourquoi. Windows attend des chaînes stockées à l'aidechar s soient encodées à l'aide de la page de codes locale. Il s'agit presque toujours du jeu de caractères ASCII suivi de 128 autres caractères spéciaux selon votre emplacement. Permettez-moi de préciser que cela ne se produit pas uniquement lors de l'utilisation de l'API Windows, il existe trois autres endroits majeurs où ces chaînes interagissent avec le C ++ standard. Ce sont des littéraux de chaîne, sortis en std::coututilisant <<et en passant un nom de fichier àstd::fstream .

Je dirai ici que je suis programmeur, pas spécialiste des langues. J'apprécie que USC2 et UTF-16 ne sont pas les mêmes, mais à mes fins, ils sont suffisamment proches pour être interchangeables et je les utilise comme tels ici. Je ne sais pas vraiment quel Windows utilise, mais je n'ai généralement pas besoin de le savoir non plus. J'ai indiqué UCS2 dans cette réponse, donc désolé à l'avance si je contrarie quelqu'un avec mon ignorance de cette question et je suis heureux de le changer si je me trompe.

Littéraux de chaîne

Si vous entrez des littéraux de chaîne qui ne contiennent que des caractères pouvant être représentés par votre page de code, VS les stocke dans votre fichier avec un codage de 1 octet par caractère basé sur votre page de code. Notez que si vous changez votre page de code ou donnez votre source à un autre développeur en utilisant une page de code différente, je pense (mais je n'ai pas testé) que le personnage finira différent. Si vous exécutez votre code sur un ordinateur en utilisant une page de codes différente, je ne sais pas si le caractère changera également.

Si vous entrez des littéraux de chaîne qui ne peuvent pas être représentés par votre page de code, VS vous demandera d'enregistrer le fichier en Unicode. Le fichier sera ensuite encodé en UTF-8. Cela signifie que tous les caractères non ASCII (y compris ceux qui se trouvent sur votre page de code) seront représentés par 2 octets ou plus. Cela signifie que si vous donnez votre source à quelqu'un d'autre, la source sera la même. Cependant, avant de transmettre la source au compilateur, VS convertit le texte codé UTF-8 en texte codé de page de code et tous les caractères manquants de la page de code sont remplacés par? .

La seule façon de garantir une représentation correcte d'un littéral de chaîne Unicode dans VS est de faire précéder le littéral de chaîne d'un en Lfaisant un littéral de chaîne large. Dans ce cas, VS convertira le texte encodé UTF-8 du fichier en UCS2. Vous devez ensuite passer ce littéral de chaîne dans un std::wstringconstructeur ou vous devez le convertir en utf-8 et le mettre dans a std::string. Ou si vous le souhaitez, vous pouvez utiliser les fonctions de l'API Windows pour l'encoder en utilisant votre page de codes pour le mettre dans unstd::string , mais vous pouvez aussi ne pas avoir utilisé un littéral de chaîne large.

std :: cout

Lors de la sortie vers la console à l'aide de, <<vous pouvez uniquement utiliser std::string, non std::wstringet le texte doit être codé à l'aide de votre page de code locale. Si vous en avez un, std::wstringvous devez le convertir à l'aide de l'une des fonctions de l'API Windows et tous les caractères ne figurant pas sur votre page de code seront remplacés par ?(vous pouvez peut-être changer le caractère, je ne me souviens pas).

std :: fstream noms de fichiers

Le système d'exploitation Windows utilise UCS2 / UTF-16 pour ses noms de fichiers, quelle que soit votre page de code, vous pouvez avoir des fichiers avec n'importe quel caractère Unicode. Mais cela signifie que pour accéder ou créer des fichiers avec des caractères ne figurant pas sur votre page de code, vous devez utiliser std::wstring. Il n'y a pas d'autre moyen. Il s'agit d'une extension spécifique à Microsoft std::fstreamqui ne se compilera donc probablement pas sur d'autres systèmes. Si vous utilisez std :: string, vous ne pouvez utiliser que des noms de fichiers qui incluent uniquement des caractères sur votre page de code.

Vos options

Si vous travaillez uniquement sur Linux, vous n'êtes probablement pas allé aussi loin. Utilisez simplement UTF-8 std::stringpartout.

Si vous travaillez uniquement sur Windows, utilisez simplement UCS2 std::wstringpartout. Certains puristes peuvent dire utiliser UTF8 puis convertir au besoin, mais pourquoi s'embêter avec les tracas.

Si vous êtes multiplateforme, c'est un gâchis d'être franc. Si vous essayez d'utiliser UTF-8 partout sur Windows, vous devez être très prudent avec vos littéraux de chaîne et la sortie vers la console. Vous pouvez facilement y corrompre vos chaînes. Si vous utilisez std::wstringpartout sur Linux, vous n'aurez peut-être pas accès à la version large de std::fstream, vous devez donc effectuer la conversion, mais il n'y a aucun risque de corruption. Personnellement, je pense que c'est une meilleure option. Beaucoup seraient en désaccord, mais je ne suis pas seul - c'est le chemin emprunté par wxWidgets par exemple.

Une autre option pourrait être de taper unicodestringcomme std::stringsous Linux et std::wstringsous Windows, et d'avoir une macro appelée UNI () qui préfixe L sous Windows et rien sous Linux, puis le code

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

serait bien sur l'une ou l'autre plate-forme, je pense.

Réponses

Alors pour répondre à vos questions

1) Si vous programmez pour Windows, alors tout le temps, si multiplateforme, peut-être tout le temps, à moins que vous ne vouliez faire face à d'éventuels problèmes de corruption sur Windows ou écrire du code avec une plate-forme spécifique #ifdefspour contourner les différences, si vous utilisez simplement Linux alors jamais.

2) Oui. De plus, sous Linux, vous pouvez également l'utiliser pour tous les Unicode. Sous Windows, vous ne pouvez l'utiliser pour tous les Unicode que si vous choisissez de coder manuellement en utilisant UTF-8. Mais l'API Windows et les classes C ++ standard s'attendent std::stringà ce qu'elles soient encodées à l'aide de la page de codes locale. Cela inclut tous les caractères ASCII et 128 autres caractères qui changent en fonction de la page de codes que votre ordinateur est configuré pour utiliser.

3) Je le crois, mais sinon, ce n'est qu'un simple typedef d'un 'std :: basic_string' utilisant wchar_tau lieu dechar

4) Un caractère large est un type de caractère plus grand que le chartype standard à 1 octet . Sous Windows, il est de 2 octets, sous Linux, il est de 4 octets.

Phil Rosenberg
la source
1
Concernant "Cependant, avant de passer la source au compilateur, VS convertit le texte codé UTF-8 en texte codé de page de code et tous les caractères manquants de la page de code sont remplacés par?." -> Je ne pense pas que ce soit vrai lorsque le compilateur utilise le codage UTF-8 (utilisation /utf-8).
Roi Danton
Je n'étais pas au courant de cette option. À partir de ce lien docs.microsoft.com/en-us/cpp/build/reference/… il semble qu'il n'y ait pas de case à cocher dans les propriétés du projet, vous devez l'ajouter comme option de ligne de commande supplémentaire. Bon endroit!
Phil Rosenberg
-2

1) Comme l'a mentionné Greg, wstring est utile pour l'internationalisation, c'est à ce moment-là que vous publierez votre produit dans des langues autres que l'anglais

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

Raghu
la source
-6

Quand ne devez-vous PAS utiliser de caractères larges?

Lorsque vous écrivez du code avant l'année 1990.

Évidemment, je suis en train de flip, mais vraiment, c'est le 21e siècle maintenant. 127 caractères ont depuis longtemps cessé d'être suffisants. Oui, vous pouvez utiliser UTF8, mais pourquoi vous embêter avec les maux de tête?


la source
16
@dave: Je ne sais pas quel mal de tête crée UTF-8 qui est supérieur à celui de Widechars (UTF-16). en UTF-16, vous avez également des caractères à plusieurs caractères.
Pavel Radzivilovsky
Le problème est que si vous êtes ailleurs que dans un pays anglophone, vous vous engagez à utiliser wchar_t. Sans oublier que certains alphabets ont beaucoup plus de caractères que vous ne pouvez en insérer dans un octet. Nous y étions, sous DOS. Codez la schizophrénie, non, merci, pas plus ..
Swift - Friday Pie
1
@Swift Le problème wchar_test que sa taille et sa signification sont spécifiques au système d'exploitation. Il échange simplement les anciens problèmes avec de nouveaux. Alors que a charest un charindépendamment de l'OS (sur des plateformes similaires, au moins). Donc, nous pourrions tout aussi bien utiliser UTF-8, tout emballer dans des séquences de chars et déplorer la façon dont C ++ nous laisse complètement seuls sans aucune méthode standard pour mesurer, indexer, trouver etc. dans de telles séquences.
underscore_d
1
@Swift Vous semblez l'avoir complètement à l'envers. wchar_test un type de données à largeur fixe, donc un tableau de 10 wchar_toccupera toujours les sizeof(wchar_t) * 10octets de la plate-forme. Et UTF-16 est un codage à largeur variable dans lequel les caractères peuvent être constitués de 1 ou 2 points de code 16 bits (et s / 16/8 / g pour UTF-8).
underscore_d
1
@SteveHollasch La représentation wchar_t de la chaîne sur les fenêtres coderait des caractères supérieurs à FFFF en tant que paire de substitution particulière, l'autre ne prendrait qu'un seul élément wchar_t. Donc, cette représentation ne sera pas compatible avec la représentation créée par le compilateur GNU (où tous les caractères inférieurs à FFFF auront zéro mot devant eux). Ce qui est stocké dans wchar_t est déterminé par le programmeur et le compilateur, et non par un accord
Swift - Friday Pie