web-dev-qa-db-fra.com

Qu'est-ce qui ne va pas avec wchar_t et wstrings C ++? Quelles sont les alternatives aux caractères larges?

J'ai vu beaucoup de gens dans la communauté C++ (en particulier ## c ++ sur freenode) en vouloir à wstrings et wchar_t, et leur utilisation dans l'API Windows. Qu'est-ce qui est "mal" avec wchar_t et wstring, et si je veux prendre en charge l'internationalisation, quelles sont les alternatives aux caractères larges?

80
Ken Li

Qu'est-ce que wchar_t?

wchar_t est défini de telle sorte que l'encodage char de n'importe quel environnement local puisse être converti en une représentation wchar_t où chaque wchar_t représente exactement un point de code:

Le 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 paramètres régionaux pris en charge (22.3.1).

- C++ [basic.fundamental] 3.9.1/5

Cela n'exige pas que wchar_t soit suffisamment grand pour représenter simultanément n'importe quel caractère de tous les paramètres régionaux. Autrement dit, l'encodage 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 un paramètre régional, puis reconvertir en char en utilisant un autre paramètre régional.1

Étant donné que l'utilisation de wchar_t comme représentation commune à tous les paramètres régionaux semble être l'utilisation principale de wchar_t dans la pratique, vous vous demandez peut-être à quoi cela sert si ce n'est pas le cas.

L'intention et le but initiaux de wchar_t étaient de simplifier le traitement de texte en le définissant de telle sorte qu'il nécessite un mappage un à un entre les unités de code d'une chaîne et les 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èse2, vous ne pouvez donc pas 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 soit comme représentation commune de texte entre des 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, puis les valeurs de wchar_t représentent directement les points de code Unicode avec les mêmes valeurs dans tous les paramètres régionaux. Cela permet de faire en toute sécurité les conversions inter-locales mentionnées précédemment. Cependant, vous ne pouvez pas vous y fier uniquement 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 dû au fait que Windows utilise UTF-16 comme encodage wchar_t et parce que UTF-16 utilise des paires de substitution pour représenter les points de code supérieurs à U + FFFF, ce qui signifie que UTF-16 ne satisfait pas aux exigences de __STDC_ISO_10646__.

Pour le code spécifique à la plate-forme, wchar_t peut être plus utile. C'est essentiellement requis sous 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 (alors peut-être pouvons-nous penser à wchar_t comme `` Windows_char_t '').

Avec le recul, wchar_t n'est clairement pas utile pour simplifier la gestion du 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. Le code non portable peut être 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 plateformes qui ne sont pas particulièrement adaptées à UTF-8.

De cette façon, on peut écrire du code portable en utilisant une représentation de texte commune sur toutes les plates-formes, utiliser des types de données standard pour leur objectif, obtenir le support du langage pour ces types (par exemple des littéraux de chaîne, bien que quelques astuces soient nécessaires pour le faire fonctionner pour certains compilateurs), certains prise en charge de bibliothèque standard, prise en charge du débogueur (d'autres astuces peuvent être nécessaires), etc. Avec des caractères larges, il est généralement plus difficile ou 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 comme ceux qui sont 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 parce que 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 natifs et de nombreux programmes ne nécessitent aucun traitement de texte important, et donc écrire un programme internationalisé sur ces plates-formes est peu différent de l'écriture de code sans envisager 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, comme des tableaux courts non signés contenant des données UTF-16, puis de fournir toute la prise en charge de la bibliothèque et de simplement vivre avec les coûts de la prise en charge des langues, 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 langue/bibliothèque associées. Ceux-ci ne sont en réalité pas garantis comme 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 UTF-8, par exemple avec les littéraux de chaîne UTF-8, il ne sera donc pas nécessaire d'inciter VC++ à produire des chaînes codées 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 est préférable de l'oublier à moins que votre programme n'ait été écrit au cours d'un millénaire précédent. Ce n'est pas portable et est intrinsèquement imprécis quant à son encodage et même son type de données, ce qui le rend inutilisable avec toute 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 aucun paramètre régional ne doivent pas ê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 pouvant être représenté par wchar_t soit suffisant pour dire que les paramètres régionaux `` prennent en charge '' ce caractère, auquel cas les encodages à largeur variable ne sont pas légaux et l'utilisation par Windows de l'UTF-16 n'est pas conforme.

2. Unicode permet à de nombreux caractères d'être représentés avec plusieurs points de code, ce qui crée les mêmes problèmes pour les algorithmes de texte simples que les encodages à largeur variable. Même si l'on maintient strictement une normalisation composée, certains caractères nécessitent toujours plusieurs points de code. Voir: http://www.unicode.org/standard/where/

112
bames53

Il n'y a rien de "mal" avec wchar_t. Le problème est que, dans NT 3.x jours, Microsoft a décidé que Unicode était bon (c'est le cas) et implémentait Unicode en tant que caractères wchar_t 16 bits. Ainsi, la plupart des publications Microsoft du milieu des années 90 assimilaient à peu près Unicode == utf16 == wchar_t.

Ce qui n'est malheureusement pas du tout le cas. Les "caractères larges" ne sont pas nécessairement 2 octets, sur toutes les plateformes, en toutes circonstances.

C'est l'une des meilleures amorces sur "Unicode" (indépendante de cette question, indépendante de C++) que j'ai jamais vue: je fortement le recommande:

Et je crois honnêtement que la meilleure façon de traiter "ASCII 8 bits" contre "Caractères larges Win32" contre "wchar_t-in-general" est simplement d'accepter que "Windows est différent" ... et de coder en conséquence.

A MON HUMBLE AVIS...

PS:

Je suis totalement d'accord avec jamesdlin ci-dessus:

Sous Windows, vous n'avez pas vraiment le choix. Ses API internes ont été conçues pour UCS-2, ce qui était raisonnable à l'époque car c'était avant que les encodages UTF-8 et UTF-16 de longueur variable soient normalisés. Mais maintenant qu'ils prennent en charge l'UTF-16, ils se sont retrouvés avec le pire des deux mondes.

18
paulsm4