web-dev-qa-db-fra.com

Que peut représenter exactement wchar_t?

Selon doc de cppreference.com sur wchar_t :

wchar_t - type de représentation des caractères larges (voir chaînes larges). Doit être suffisamment grand pour représenter tout point de code de caractère pris en charge (32 bits sur les systèmes qui prennent en charge Unicode. Une exception notable est Windows, où wchar_t est de 16 bits et contient des unités de code UTF-16). Il a la même taille, la signature et l'alignement comme l'un des types entiers, mais est un type distinct.

La norme indique dans [basic.fundamental]/5 :

Tapez 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. Tapez wchar_­t doit avoir les mêmes exigences de taille, de signature et d'alignement que l'un des autres types intégraux, appelé son type sous-jacent. Les types char16_­t et char32_­t désigne des types distincts ayant la même taille, la même signature et le même alignement que uint_­least16_­t et uint_­least32_­t, respectivement, dans <cstdint>, appelé les types sous-jacents.

Donc, si je veux traiter des caractères unicode, dois-je utiliser wchar_t?

De manière équivalente, comment savoir si un caractère Unicode spécifique est "pris en charge" par wchar_t?

22
YSC

Donc, si je veux traiter des caractères unicode, dois-je utiliser wchar_t?

Tout d'abord, notez que l'encodage ne vous oblige pas à utiliser un type particulier pour représenter un certain caractère. Vous pouvez utiliser char pour représenter les caractères Unicode tout comme wchar_t peut - vous devez seulement vous rappeler que jusqu'à 4 char ensemble formeront un point de code valide en fonction du codage UTF-8, UTF-16 ou UTF-32, tandis que wchar_t peut utiliser 1 (UTF-32 sous Linux, etc.) ou jusqu'à 2 en collaboration (UTF-16 sous Windows).

Ensuite, il n'y a pas de codage Unicode défini. Certains encodages Unicode utilisent une largeur fixe pour représenter les points de code (comme UTF-32), d'autres (tels que UTF-8 et UTF-16) ont des longueurs variables (la lettre 'a' par exemple n'utilisera sûrement qu'un octet, mais à part de l'alphabet anglais, d'autres caractères utiliseront sûrement plus d'octets pour la représentation).

Vous devez donc décider du type de caractères que vous souhaitez représenter, puis choisir votre encodage en conséquence. Selon le type de caractères que vous souhaitez représenter, cela affectera la quantité d'octets que prendront vos données. Par exemple. utiliser UTF-32 pour représenter principalement des caractères anglais conduira à de nombreux octets. UTF-8 est un meilleur choix pour de nombreuses langues basées sur le latin, tandis que UTF-16 est généralement un meilleur choix pour les langues d'Asie orientale.

Une fois que vous avez décidé de cela, vous devez minimiser le nombre de conversions et rester cohérent avec votre décision.

À l'étape suivante, vous pouvez décider du type de données approprié pour représenter les données (ou du type de conversions dont vous pourriez avoir besoin).

Si vous souhaitez faire de la manipulation/interprétation de texte sur une base de point de code, char n'est certainement pas la voie à suivre si vous avez par exemple Kanji japonais. Mais si vous voulez simplement communiquer vos données et ne plus les considérer comme une séquence quantitative d'octets, vous pouvez simplement utiliser char.

Le lien vers TF-8 partout a déjà été posté en tant que commentaire, et je vous suggère d'y jeter un œil également. Une autre bonne lecture est Ce que tout programmeur doit savoir sur les encodages .

Comme maintenant, il n'y a qu'un support de langage rudimentaire en C++ pour Unicode (comme le char16_t et char32_t types de données et u8/u/U préfixes littéraux). Donc, choisir une bibliothèque pour gérer les encodages (en particulier les conversions) est certainement un bon conseil.

13
Jodocus

wchar_t Est utilisé dans Windows qui utilise le format UTF16-LE. wchar_t Nécessite de larges fonctions de caractères. Par exemple wcslen(const wchar_t*) au lieu de strlen(const char*) et std::wstring Au lieu de std::string

Les machines basées sur Unix (Linux, Mac, etc.) utilisent UTF8. Cela utilise char pour le stockage, et les mêmes fonctions C et C++ pour ASCII, telles que strlen(const char*) et std::string (Voir les commentaires ci-dessous sur std::find_first_of)

wchar_t Est de 2 octets (UTF16) sous Windows. Mais dans d'autres machines, il est de 4 octets (UTF32). Cela rend les choses plus confuses.

Pour UTF32, vous pouvez utiliser std::u32string Qui est le même sur différents systèmes.


Vous pourriez envisager de convertir UTF8 en UTF32, car de cette façon, chaque caractère fait toujours 4 octets, et vous pourriez penser que les opérations de chaîne seront plus faciles. Mais c'est rarement nécessaire.

UTF8 est conçu pour que les caractères ASCII entre 0 et 128 ne soient pas utilisés pour représenter d'autres points de code Unicode. Cela inclut la séquence d'échappement '\', printf spécificateurs de format, et des caractères d'analyse communs comme ,

Considérez la chaîne UTF8 suivante. Disons que vous voulez trouver la virgule

std::string str = u8"汉,????"; //3 code points represented by 8 bytes

La valeur ASCII pour la virgule est 44 Et str ne peut contenir qu'un seul octet dont la valeur est 44. Pour trouver la virgule , vous pouvez simplement utiliser n'importe quelle fonction standard en C ou C++ pour rechercher ','

Pour trouver , Vous pouvez rechercher la chaîne u8"汉" Car ce point de code ne peut pas être représenté comme un seul caractère.

Certaines fonctions C et C++ ne fonctionnent pas correctement avec UTF8. Ceux-ci inclus

strtok
strspn
std::find_first_of

L'argument pour les fonctions ci-dessus est un ensemble de caractères, pas une chaîne réelle.

Donc, str.find_first_of(u8"汉") ne fonctionne pas. Parce que u8"汉" Fait 3 octets, et find_first_of Recherchera l'un de ces octets. Il est possible qu'un de ces octets soit utilisé pour représenter un point de code différent.

D'un autre côté, str.find_first_of(u8",;abcd") est sûr, car tous les caractères de l'argument de recherche sont ASCII (str lui-même peut contenir n'importe quel caractère Unicode)

Dans de rares cas, UTF32 peut être requis (bien que je ne puisse pas imaginer où!) Vous pouvez utiliser std::codecvt Pour convertir UTF8 en UTF32 pour exécuter les opérations suivantes:

std::u32string u32 = U"012汉"; //4 code points, represented by 4 elements
cout << u32.find_first_of(U"汉") << endl; //outputs 3
cout << u32.find_first_of(U'汉') << endl; //outputs 3

Note latérale:

Vous devez utiliser "Unicode partout" , pas "UTF8 partout" .

Sous Linux, Mac, etc., utilisez UTF8 pour Unicode.

Sous Windows, utilisez UTF16 pour Unicode. Les programmeurs Windows utilisent UTF16, ils ne font pas de conversions inutiles en UTF8. Mais il existe des cas légitimes pour utiliser UTF8 dans Windows.

Les programmeurs Windows ont tendance à utiliser UTF8 pour enregistrer des fichiers, des pages Web, etc. C'est donc moins de souci pour les programmeurs non Windows en termes de compatibilité.

Le langage lui-même ne se soucie pas du format Unicode que vous souhaitez utiliser, mais en termes pratiques, utilisez un format qui correspond au système sur lequel vous travaillez.

8
Barmak Shemirani

Donc, si je veux traiter des caractères unicode, dois-je utiliser wchar_t?

Cela dépend de l'encodage avec lequel vous traitez. Dans le cas de l'UTF-8, vous êtes très bien avec char et std :: string. UTF -8 signifie que la plus petite unité de codage est de 8 bits: tous les points de code Unicode de U + 0000 à U + 007F sont codés par seulement 1 octet. À partir du point de code U + 0080 UTF-8 utilise 2 octets pour l'encodage, à partir de U + 0800, il utilise 3 octets et de U + 10000 4 octets. Pour gérer cette largeur variable (1 octet - 2 octets - 3 octets - 4 octets), le caractère convient le mieux. Sachez que les fonctions C comme strlen fourniront des résultats basés sur des octets: "öö" est en fait un texte à 2 caractères mais strlen renverra 4 car 'ö' est codé en 0xC3B6.

UTF -16 signifie que la plus petite unité de codage est de 16 bits: tous les points de code de U + 0000 à U + FFFF sont codés sur 2 octets; à partir de U + 100000 4 octets sont utilisés. Dans le cas d'UTF-16, vous devez utiliser wchar_t et std :: wstring car la plupart des caractères que vous rencontrerez seront codés sur 2 octets. Lorsque vous utilisez wchar_t, vous ne pouvez plus utiliser les fonctions C comme strlen; vous devez utiliser les équivalents larges comme wcslen.

Lorsque vous utilisez Visual Studio et que vous construisez avec la configuration "Unicode", vous obtiendrez UTF-16: TCHAR et CString seront basés sur wchar_t au lieu de char.

5
testalucida

Tout dépend de ce que vous entendez par "traiter", mais une chose est sûre: en ce qui concerne Unicode std::basic_string Ne fournit aucune fonctionnalité réelle.

Dans tout programme particulier, vous devrez effectuer un nombre X d'opérations compatibles Unicode, par ex. correspondance de chaîne intelligente, pliage de casse, expression régulière, localisation de sauts de mot, utilisation d'une chaîne Unicode comme nom de chemin, etc.

En soutenant ces opérations, il y aura presque toujours une sorte de bibliothèque et/ou d'API native fournie par la plateforme, et l'objectif pour moi serait de stocker et de manipuler mes chaînes de manière à ce que ces opérations puissent être effectuées sans disperser la connaissance de la bibliothèque sous-jacente et le support de l'API native dans tout le code plus que nécessaire. Je voudrais également me préparer à l'avenir quant à la largeur des caractères que je stocke dans mes chaînes au cas où je changerais d'avis.

Supposons, par exemple, que vous décidiez d'utiliser ICU pour faire le gros du travail. Immédiatement, il y a un problème évident: un icu::UnicodeString N'est en aucun cas lié à std::basic_string. Que faire? Travailler exclusivement avec icu::UnicodeString Dans tout le code? Probablement pas.

Ou peut-être que le centre d'intérêt de l'application passe des langues européennes aux langues asiatiques, de sorte que l'UTF-16 devient (peut-être) un meilleur choix que l'UTF-8.

Donc, mon choix serait d'utiliser une classe de chaîne personnalisée dérivée de std::basic_string, Quelque chose comme ceci:

typedef wchar_t mychar_t;  // say

class MyString : public std::basic_string <mychar_t>
{
...
};

Vous avez tout de suite la flexibilité de choisir la taille des unités de code stockées dans votre conteneur. Mais vous pouvez faire bien plus que cela. Par exemple, avec la déclaration ci-dessus (et après avoir ajouté un passe-partout pour les différents constructeurs que vous devez fournir pour les transmettre à std::basic_string), Vous ne pouvez toujours pas dire:

MyString s = "abcde";

Parce que "abcde" est une chaîne étroite et divers constructeurs pour std::basic_string <wchar_t> Attendent tous une chaîne large. Microsoft résout ce problème avec une macro (TEXT ("...") ou __T ("...")), mais c'est pénible. Tout ce que nous devons faire maintenant est de fournir un constructeur approprié dans MyString, avec la signature MyString (const char *s), et le problème est résolu.

En pratique, ce constructeur attendrait probablement une chaîne UTF-8, quelle que soit la largeur de caractère sous-jacente utilisée pour MyString, et la convertirait si nécessaire. Quelqu'un commente ici quelque part que vous devez stocker vos chaînes au format UTF-8 afin de pouvoir les construire à partir de littéraux UTF-8 dans votre code. Eh bien maintenant, nous avons brisé cette contrainte. La largeur de caractère sous-jacente de nos chaînes peut être tout ce que nous aimons.

Une autre chose dont les gens ont parlé dans ce fil est que find_first_of Peut ne pas fonctionner correctement pour les chaînes UTF-8 (et en fait certaines UTF-16 également). Eh bien, vous pouvez maintenant fournir une implémentation qui fait le travail correctement. Cela devrait prendre environ une demi-heure. S'il y a d'autres implémentations "cassées" dans std::basic_string (Et je suis sûr qu'il y en a), alors la plupart d'entre elles peuvent probablement être remplacées avec la même facilité.

Pour le reste, cela dépend principalement du niveau d'abstraction que vous souhaitez implémenter dans votre classe MyString. Si votre application est satisfaite d'avoir une dépendance à ICU, par exemple, alors vous pouvez simplement fournir quelques méthodes pour convertir vers et depuis un icu::UnicodeString. C'est probablement ce que la plupart des gens feraient.

Ou si vous devez passer des chaînes UTF-16 vers/depuis les API Windows natives, vous pouvez ajouter des méthodes pour convertir vers et depuis const WCHAR * (Que vous implémenteriez à nouveau de telle manière qu'elles fonctionnent pour toutes les valeurs de mychar_t ). Ou vous pouvez aller plus loin et résumer tout ou partie du support Unicode fourni par la plateforme et la bibliothèque que vous utilisez. Le Mac, par exemple, a une prise en charge Unicode riche, mais il n'est disponible qu'à partir d'Objective-C, vous devez donc l'envelopper. Cela dépend de la façon dont vous voulez que votre code soit portable.

Vous pouvez donc ajouter toutes les fonctionnalités que vous souhaitez, probablement sur une base continue au fur et à mesure que le travail progresse, sans perdre la possibilité de transporter vos chaînes comme un std::basic_string. D'une sorte ou d'une autre. Essayez simplement de ne pas écrire de code qui suppose qu'il sait sa largeur ou qu'il ne contient pas paires de substitution .

4
Paul Sanders

Tout d'abord, vous devez vérifier (comme vous le signalez dans votre question) si vous utilisez Windows et Visual Studio C++ avec wchar_t Étant 16 bits, car dans ce cas, pour utiliser la prise en charge unicode complète, vous aurez besoin pour assumer le codage UTF-16.

Le problème de base ici n'est pas le sizeof wchar_t Que vous utilisez, mais si les bibliothèques que vous allez utiliser prennent en charge la prise en charge complète d'Unicode.

Java a un problème similaire, car son type char a une largeur de 16 bits, il ne pouvait donc pas a priori prendre en charge l'espace unicode complet, mais il le fait, car il utilise le codage UTF-16 et la paire de substituts pour faire face aux points de code 24 bits complets.

Il convient également de noter que UNICODE utilise uniquement le plan supérieur pour coder les points de code rares, qui ne sont normalement pas utilisés quotidiennement.

Pour la prise en charge unicode de toute façon, vous devez utiliser des jeux de caractères larges, donc wchar_t Est un bon début. Si vous allez travailler avec Visual Studio, vous devez vérifier comment ses bibliothèques gèrent les caractères Unicode.

Une autre chose à noter est que les bibliothèques standard traitent les jeux de caractères (et cela inclut unicode) uniquement lorsque vous ajoutez la prise en charge des paramètres régionaux (cela nécessite l'initialisation d'une bibliothèque, par exemple setlocale(3)) et donc, vous ne verrez pas unicode du tout (uniquement ascii de base) dans les cas où vous n'avez pas appelé setlocale(3).

Il existe de nombreuses fonctions char pour presque toutes les fonctions str*(3), ainsi que pour toute fonction de bibliothèque stdio.h, Pour gérer wchar_t S. Une petite fouille dans le fichier /usr/include/wchar.h Révèlera les noms des routines. Accédez aux pages de manuel pour obtenir une documentation à leur sujet: fgetws(3), fputwc(3), fputws(3), fwide(3), fwprintf(3), ...

Enfin, considérez à nouveau que, si vous traitez avec Microsoft Visual C++, vous avez une implémentation différente depuis le début. Même s'ils sont entièrement conformes aux normes , vous devrez faire face à certaines particularités d'avoir une implémentation différente. Vous aurez probablement différents noms de fonction pour certaines utilisations.

3
Luis Colorado