31 votes

Comment utiliser Unicode en C++ ?

En supposant un programme très simple qui :

  • demander un nom.
  • stocker le nom dans une variable.
  • afficher le contenu de la variable sur l'écran.

C'est tellement simple que c'est la première chose que l'on apprend.

Mais mon problème est que je ne sais pas comment faire la même chose si j'entre le nom en utilisant des caractères japonais.

Donc, si vous savez comment faire cela en C++, veuillez me montrer un exemple (que je puisse compiler et tester).

Merci.


user362981 : Merci pour votre aide. J'ai compilé le code que vous avez écrit sans problème, ils la fenêtre de la console apparaît et je ne peux pas entrer de caractères japonais sur elle (en utilisant IME). Aussi si Je change un mot dans votre code ("hello") en un mot qui contient des caractères japonais, il ne les affichera pas non plus.

Svisstack : Merci également pour votre aide. Mais lorsque je compile votre code, j'obtiens l'erreur suivante :

warning: deprecated conversion from string constant to 'wchar_t*'
error: too few arguments to function 'int swprintf(wchar_t*, const wchar_t*, ...)'
error: at this point in file
warning: deprecated conversion from string constant to 'wchar_t*'

45voto

Thanatos Points 13444

Vous allez obtenir beaucoup de réponses sur les grands personnages. Les caractères larges, plus précisément wchar_t ne sont pas égales à Unicode . Vous pouvez les utiliser (avec certains écueils) pour stocker l'Unicode, tout comme vous pouvez utiliser un fichier unsigned char . wchar_t est extrêmement dépendant du système. Pour citer le Norme Unicode, version 5.2, chapitre 5 :

Avec le wchar_t large, l'ANSI/ISO C prévoit un type de caractère l'inclusion de caractères larges de largeur fixe. L'ANSI/ISO C laisse la sémantique du type de caractère large à la discrétion de l'utilisateur. à l'implémentation spécifique mais exige que les caractères de l'ensemble d'exécution C portable correspondent à leurs équivalents de caractères larges par extension zéro.

et que

La largeur de wchar_t est spécifique au compilateur et peut être aussi petite que 8 bits. Par conséquent, les programmes qui doivent être portables à travers n'importe quel compilateur C ou C++ ne doivent pas utiliser wchar_t pour stocker du texte Unicode. Le site wchar_t est destiné à stocker les caractères larges définis par le compilateur. définis par le compilateur, qui peuvent être des caractères Unicode dans certains compilateurs.

Donc, c'est une mise en œuvre définie. Voici deux implémentations : Sous Linux, wchar_t a une largeur de 4 octets, et représente le texte dans l'encodage UTF-32 (indépendamment de la locale actuelle). (Soit BE ou LE selon votre système, celui qui est natif.) Windows, cependant, a un codage de 2 octets de large. wchar_t et représente les unités de code UTF-16 avec elles. Complètement différent.

Un meilleur chemin : Apprenez à connaître les locales, car vous aurez besoin de les connaître. Par exemple, parce que J'ai configuré mon environnement pour utiliser UTF-8 (Unicode), le programme suivant utilisera Unicode :

#include <iostream>

int main()
{
    setlocale(LC_ALL, "");
    std::cout << "What's your name? ";
    std::string name;
    std::getline(std::cin, name);
    std::cout << "Hello there, " << name << "." << std::endl;
    return 0;
}

...

$ ./uni_test
What's your name? 佐藤 幹夫
Hello there, 佐藤 幹夫.
$ echo $LANG
en_US.UTF-8

Mais il n'y a rien d'Unicode là-dedans. Il ne fait que lire les caractères, qui arrivent en UTF-8. parce que mon environnement est configuré de cette façon . Je pourrais tout aussi bien dire "bon sang, je suis en partie tchèque, utilisons ISO-8859-2" : Soudain, le programme reçoit des données en ISO-8859-2, mais comme il ne fait que les régurgiter, cela n'a pas d'importance, le programme continuera à fonctionner correctement.

Maintenant, si cet exemple avait lu mon nom, et avait ensuite essayé de l'écrire dans un fichier XML, et avait stupidement écrit <?xml version="1.0" encoding="UTF-8" ?> en haut, il serait correct lorsque mon terminal est en UTF-8, mais faux lorsque mon terminal est en ISO-8859-2. Dans ce dernier cas, il faudrait le convertir avant de le sérialiser dans le fichier XML. (Ou alors, écrivez simplement ISO-8859-2 comme encodage pour le fichier XML).

Sur de nombreux systèmes POSIX, la locale courante est généralement UTF-8, car elle offre plusieurs avantages à l'utilisateur, mais ce n'est pas garanti. Le simple fait d'envoyer UTF-8 à stdout sera généralement correcte, mais pas toujours. Imaginons que j'utilise ISO-8859-2 : si vous produisez sans réfléchir un "è" ISO-8859-1 ( 0xE8 ) à mon terminal, je verrai un "č" ( 0xE8 ). De même, si vous produisez un "è" UTF-8 ( 0xC3 0xA8 ), je verrai (ISO-8859-2) "è" ( 0xC3 0xA8 ). Cette dégueulasserie de caractères incorrects a été appelée Mojibake .

Souvent, vous ne faites que déplacer des données, et cela n'a pas beaucoup d'importance. Cela se produit généralement lorsque vous devez sérialiser des données. (De nombreux protocoles Internet utilisent UTF-8 ou UTF-16, par exemple : si vous recevez des données d'un terminal ISO-8859-2, ou d'un fichier texte encodé en Windows-1252, vous devez les convertir, sinon vous enverrez Mojibake .)

Malheureusement, il s'agit de l'état de la prise en charge d'Unicode, tant en C qu'en C++. Il faut se rappeler que ces langages sont vraiment agnostiques vis-à-vis des systèmes et ne sont liés à aucune façon particulière de faire les choses. Cela inclut les jeux de caractères. Il existe cependant des tonnes de bibliothèques permettant de gérer Unicode et d'autres jeux de caractères.

En fin de compte, ce n'est pas si compliqué que ça : Sachez quel est l'encodage de vos données, et sachez quel devrait être l'encodage de votre sortie. S'ils ne sont pas identiques, vous devez effectuer une conversion. Ceci est valable que vous utilisiez std::cout o std::wcout . Dans mes exemples, stdin o std::cin y stdout / std::cout étaient parfois en UTF-8, parfois en ISO-8859-2.

2voto

EvanED Points 150

Essayez de remplacer cout par wcout, cin par wcin, et string par wstring. En fonction de votre plate-forme, cela peut fonctionner :

#include <iostream>
#include <string>

int main() {
  std::wstring name;
  std::wcout << L"Enter your name: "; 
  std::wcin >> name;
  std::wcout << L"Hello, " << name << std::endl;
}

Il existe d'autres moyens, mais c'est en quelque sorte la réponse "changement minimal".

2voto

zadane Points 934

Pré-requis : http://www.joelonsoftware.com/articles/Unicode.html

L'article ci-dessus est une lecture indispensable qui explique ce qu'est l'unicode mais quelques questions subsistent. Oui, l'UNICODE a un point de code unique pour chaque caractère dans chaque langue et, en outre, ils peuvent être codés et stockés en mémoire potentiellement différemment de ce que le code réel est. De cette façon, nous pouvons économiser de la mémoire en utilisant par exemple l'encodage UTF-8, ce qui est très bien si la langue supportée est seulement l'anglais et que la représentation en mémoire est essentiellement la même que celle de l'ASCII - ceci bien sûr en connaissant l'encodage lui-même. En théorie, si nous connaissons le codage, nous pouvons stocker ces caractères UNICODE plus longs comme bon nous semble et les relire. Mais le monde réel est un peu différent.

Comment stocker un caractère/chaîne UNICODE dans un programme C++ ? Quel encodage utilisez-vous ? La réponse est que vous n'utilisez pas d'encodage mais que vous stockez directement les points de code UNICODE dans une chaîne de caractères unicode, tout comme vous stockez les caractères ASCII dans une chaîne ASCII. La question est de savoir quelle taille de caractère vous devez utiliser puisque les caractères UNICODE n'ont pas de taille fixe. La réponse est simple : vous choisissez le corps de caractère qui est suffisamment large pour contenir le point de code de caractère le plus élevé (langue) que vous souhaitez prendre en charge.

La théorie selon laquelle un caractère UNICODE peut prendre 2 octets ou plus est toujours valable et cela peut créer une certaine confusion. Ne devrions-nous pas stocker les points de code dans 3 ou 4 octets, ce qui représente réellement tous les caractères unicode ? Pourquoi Visual C++ stocke-t-il l'unicode dans wchar_t, qui n'occupe que 2 octets, ce qui est clairement insuffisant pour stocker tous les points de code UNICODE ?

La raison pour laquelle nous stockons le point de code du caractère UNICODE sur 2 octets dans Visual C++ est en fait exactement la même raison pour laquelle nous stockions le caractère ASCII (=anglais) sur un octet. À l'époque, nous ne pensions qu'à l'anglais et un seul octet était suffisant. Aujourd'hui, nous pensons à la plupart des langues internationales, mais pas à toutes, et nous utilisons donc deux octets, ce qui est suffisant. Il est vrai que cette représentation ne nous permettra pas de représenter les points de code qui nécessitent 3 octets ou plus, mais nous ne nous en soucions pas encore, car ces personnes n'ont même pas encore acheté d'ordinateur. Oui, nous n'utilisons pas 3 ou 4 octets parce que nous sommes toujours avares de mémoire, pourquoi stocker l'octet supplémentaire 0 (zéro) avec chaque caractère si nous ne l'utilisons jamais (ce langage). Encore une fois, c'est exactement pour les mêmes raisons que l'ASCII stockait chaque caractère dans un octet, pourquoi stocker un caractère dans 2 octets ou plus lorsque l'anglais peut être représenté dans un octet et qu'il reste de la place pour ces caractères spéciaux supplémentaires !

En théorie, deux octets ne suffisent pas à présenter tous les points de code Unicode, mais ils sont suffisants pour contenir tout ce qui peut nous intéresser pour l'instant. Une véritable représentation de chaîne UNICODE pourrait stocker chaque caractère sur 4 octets, mais ces langues ne nous intéressent pas.

Imaginez que dans 1000 ans, nous trouvions des extraterrestres amicaux et en abondance et que nous voulions communiquer avec eux en incorporant leurs innombrables langues. La taille d'un seul caractère unicode augmentera encore, peut-être jusqu'à 8 octets, afin de prendre en compte tous leurs points de code. Cela ne signifie pas que nous devrions commencer à utiliser 8 octets pour chaque caractère unicode dès maintenant. La mémoire est une ressource limitée, nous allouons ce dont nous avons besoin.

Puis-je traiter une chaîne UNICODE comme une chaîne de style C ?

Une chaîne ASCII peut encore être manipulée en C++ et c'est assez courant en la saisissant par son pointeur char * où les fonctions C peuvent être appliquées. Cependant, l'application des fonctions C actuelles sur une chaîne UNICODE n'aura aucun sens car elle peut contenir un seul octet NULL qui met fin à une chaîne C.

Une chaîne de caractères UNICODE n'est plus un simple tampon de texte, c'est vrai mais c'est maintenant plus compliqué qu'un flux de caractères d'un seul octet se terminant par un octet NULL. Ce tampon peut être géré par son pointeur, même en C, mais il faudra un appel compatible UNICODE ou une bibliothèque C qui pourra lire et écrire ces chaînes et effectuer des opérations.

Cela est facilité en C++ par une classe spécialisée qui représente une chaîne UNICODE. Cette classe gère la complexité du tampon de la chaîne unicode et fournit une interface facile. Cette classe décide également si chaque caractère de la chaîne unicode est de 2 octets ou plus - ce sont des détails d'implémentation. Aujourd'hui, elle peut utiliser wchar_t (2 octets) mais demain, elle pourra utiliser 4 octets pour chaque caractère afin de prendre en charge un plus grand nombre de langues (moins connues). C'est pourquoi il est toujours préférable d'utiliser TCHAR plutôt qu'une taille fixe qui correspond à la bonne taille lorsque la mise en œuvre change.

Comment indexer une chaîne UNICODE ?

Il est également intéressant de noter, en particulier dans la gestion des chaînes de caractères en C, qu'ils utilisent l'index pour parcourir ou trouver une sous-chaîne dans une chaîne de caractères. Cet indice dans une chaîne ASCII correspond directement à la position de l'élément dans cette chaîne, mais il n'a aucune signification dans une chaîne UNICODE et doit être évité.

Que devient l'octet NULL de fin de chaîne ?

Les chaînes UNICODE sont-elles toujours terminées par l'octet NULL ? Un seul octet NULL suffit-il pour terminer la chaîne ? C'est une question d'implémentation mais un octet NULL est toujours un point de code Unicode et comme tous les autres points de code, il doit avoir la même taille que tous les autres (spécialement en l'absence d'encodage). Ainsi, le caractère NULL doit également être de deux octets si l'implémentation de la chaîne unicode est basée sur wchar_t. Tous les points de code UNICODE seront représentés par la même taille, qu'il s'agisse d'un octet nul ou d'un autre.

Est-ce que Visual C++ Debugger affiche du texte UNICODE ?

Oui, si le tampon de texte est de type LPWSTR ou de tout autre type prenant en charge UNICODE, Visual Studio 2005 et les versions ultérieures prennent en charge l'affichage du texte international dans la fenêtre de surveillance du débogueur (à condition que les polices et les packs de langue soient installés, bien sûr).

Résumé :

Le C++ n'utilise pas d'encodage pour stocker les caractères unicode mais il stocke directement les points de code UNICODE pour chaque caractère dans une chaîne. Il doit choisir une taille de caractère suffisamment grande pour contenir le plus grand caractère des langues souhaitées (au sens large) et cette taille de caractère sera fixée et utilisée pour tous les caractères de la chaîne.

Actuellement, 2 octets sont suffisants pour représenter la plupart des langues qui nous intéressent, c'est pourquoi 2 octets sont utilisés pour représenter le point de code. À l'avenir, si une nouvelle colonie spatiale amie est découverte et que nous voulons communiquer avec elle, nous devrons attribuer de nouveaux points de code Unicode à sa langue et utiliser une taille de caractères plus grande pour stocker ces chaînes.

1voto

Svisstack Points 9001
#include <stdio.h>
#include <wchar.h>

int main()
{
    wchar_t name[256];

    wprintf(L"Type a name: ");
    wscanf(L"%s", name);

    wprintf(L"Typed name is: %s\n", name);

    return 0;
}

1voto

Nick Bastin Points 12627

Vous pouvez faire des choses simples avec le support générique des caractères larges dans le système d'exploitation de votre choix, mais en général, le C++ n'a pas un bon support intégré pour l'unicode, donc vous serez mieux à long terme en regardant quelque chose comme UNITÉ DE SOINS INTENSIFS .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X