J'ai vu beaucoup de gens dans la communauté C ++ (en particulier ## c ++ sur freenode) se plaindre de l'utilisation de wstrings
and wchar_t
, et de leur utilisation dans l'API Windows. Qu'est-ce qui ne va pas exactement avec wchar_t
et wstring
, et si je veux soutenir l'internationalisation, quelles sont les alternatives aux caractères larges?
86
Réponses:
Qu'est-ce que wchar_t?
wchar_t est défini de telle sorte que le codage char de tout paramètre régional peut être converti en une représentation wchar_t où chaque wchar_t représente exactement un point de code:
Cela ne nécessite pas que wchar_t soit suffisamment grand pour représenter un caractère de tous les paramètres régionaux simultanément. Autrement dit, le codage 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 une locale, puis la reconvertir en char en utilisant une autre locale. 1
Puisque l'utilisation de wchar_t comme représentation commune entre tous les paramètres régionaux semble être la principale utilisation de wchar_t dans la pratique, vous vous demandez peut-être à quoi cela sert sinon cela.
L'intention et le but d'origine de wchar_t était de simplifier le traitement de texte en le définissant de telle sorte qu'il nécessite un mappage un-à-un des unités de code d'une chaîne aux caractères du texte, permettant ainsi l'utilisation des mêmes algorithmes simples que ceux utilisés avec des chaînes ascii pour travailler avec d'autres langues.
Malheureusement, le libellé de la spécification de wchar_t suppose un mappage un à un entre les caractères et les points de code pour y parvenir. Unicode rompt cette hypothèse 2 , vous ne pouvez donc pas non plus 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 ni comme représentation commune du texte entre les 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, alors les valeurs de wchar_t représentent directement des points de code Unicode avec les mêmes valeurs dans tous les paramètres régionaux. Cela permet d'effectuer en toute sécurité les conversions inter-locales mentionnées précédemment. Cependant, vous ne pouvez pas vous fier uniquement à lui 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__
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 des points de code supérieurs à U + FFFF, ce qui signifie que UTF-16 ne répond pas aux exigences de__STDC_ISO_10646__
.Pour le code spécifique à la plate-forme, wchar_t peut être plus utile. C'est essentiellement nécessaire 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 clairement pas utile pour simplifier la gestion de 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. Un code non portable peut le trouver utile simplement parce que certaines API l'exigent.
Alternatives
L'alternative que j'aime est d'utiliser des chaînes C encodées en UTF-8, même sur des plates-formes pas particulièrement adaptées à UTF-8.
De cette façon, on peut écrire du code portable en utilisant une représentation textuelle commune sur toutes les plates-formes, utiliser des types de données standard pour leur usage prévu, obtenir le support du langage pour ces types (par exemple, des chaînes littérales, bien que certaines astuces soient nécessaires pour le faire fonctionner pour certains compilateurs), certains support de bibliothèque standard, support de débogueur (plus de trucs peuvent être nécessaires), etc. Avec des caractères larges, il est généralement plus difficile voire impossible d'obtenir tout cela, et vous pouvez obtenir différentes pièces sur différentes plates-formes.
Une chose que UTF-8 ne fournit pas est la possibilité d'utiliser des algorithmes de texte simples tels que ceux possibles avec ASCII. Dans cet UTF-8 n'est pas pire que tout autre encodage Unicode. En fait, cela peut être considéré comme meilleur car les représentations d'unité multi-code en UTF-8 sont plus courantes et donc les bogues dans la gestion du code de telles représentations de caractères à largeur variable sont plus susceptibles d'être remarqués et corrigés que si vous essayez de vous en tenir à UTF -32 avec NFC ou NFKC.
De nombreuses plates-formes utilisent UTF-8 comme encodage de caractères natif et de nombreux programmes ne nécessitent pas de traitement de texte significatif, et donc l'écriture d'un programme internationalisé sur ces plates-formes est peu différente de l'écriture de code sans considérer l'internationalisation. L'écriture de code plus largement portable ou l'écriture sur d'autres plates-formes nécessite l'insertion de conversions aux limites des API qui utilisent d'autres encodages.
Une autre alternative utilisée par certains logiciels est de choisir une représentation multiplateforme, telle que des tableaux courts non signés contenant des données UTF-16, puis de fournir tout le support de la bibliothèque et de vivre simplement avec les coûts de prise en charge de la langue, etc.
C ++ 11 ajoute de nouveaux types de caractères larges comme alternatives à wchar_t, char16_t et char32_t avec des fonctionnalités de langage / bibliothèque. Il n'est pas garanti que ce soit UTF-16 et UTF-32, mais je n'imagine pas qu'une implémentation majeure utilisera autre chose. C ++ 11 améliore également la prise en charge de UTF-8, par exemple avec les littéraux de chaîne UTF-8, il ne sera donc pas nécessaire de tromper VC ++ en produisant des chaînes encodées en UTF-8 (bien que je puisse continuer à le faire plutôt que d'utiliser le
u8
préfixe) .Alternatives à éviter
TCHAR: TCHAR est destiné à la migration d'anciens programmes Windows qui supposent des encodages hérités de char vers wchar_t, et il vaut mieux l'oublier à moins que votre programme n'ait été écrit dans un millénaire précédent. Il n'est pas portable et est intrinsèquement non spécifique quant à son encodage et même à son type de données, ce qui le rend inutilisable avec une API non basée sur TCHAR. Puisque son but est la migration vers wchar_t, ce que nous avons vu ci-dessus n'est pas une bonne idée, il n'y a aucune valeur à utiliser TCHAR.
1. Les caractères qui sont représentables dans les chaînes wchar_t mais qui ne sont pris en charge dans aucune locale ne sont pas obligés d'être représentés avec une seule valeur wchar_t. Cela signifie que wchar_t pourrait utiliser un codage à largeur variable pour certains caractères, une autre violation claire de l'intention de wchar_t. Bien qu'il soit discutable qu'un caractère représentable par wchar_t soit suffisant pour dire que la locale `` prend en charge '' ce caractère, auquel cas les encodages à largeur variable ne sont pas légaux et l'utilisation par Window de UTF-16 n'est pas conforme.
2. Unicode permet de représenter de nombreux caractères avec plusieurs points de code, ce qui crée les mêmes problèmes pour les algorithmes de texte simples que pour les encodages à largeur variable. Même si l'on maintient strictement une normalisation composée, certains caractères nécessitent encore plusieurs points de code. Voir: http://www.unicode.org/standard/where/
la source
fopen
un fichier dont le nom contient des caractères non ANSI.Il n'y a rien de "mal" avec wchar_t. Le problème est que, à l'époque de NT 3.x, Microsoft a décidé qu'Unicode était bon (c'est le cas) et d'implémenter Unicode sous forme de caractères 16 bits wchar_t. Ainsi, la plupart des publications Microsoft du milieu des années 90 assimilaient à peu près Unicode == utf16 == wchar_t.
Ce qui, malheureusement, n’est pas du tout le cas. Les «caractères larges» ne sont pas forcément 2 octets, sur toutes les plateformes, dans toutes les circonstances.
C'est l'une des meilleures amorces sur "Unicode" (indépendante de cette question, indépendante du C ++) que j'ai jamais vue: je le recommande vivement :
Et je crois honnêtement que la meilleure façon de traiter "ASCII 8 bits" vs "caractères Win32 larges" vs "wchar_t-en-général" est simplement d'accepter que "Windows est différent" ... et coder en conséquence.
A MON HUMBLE AVIS...
PS:
Je suis totalement d'accord avec jamesdlin ci-dessus:
la source