web-dev-qa-db-fra.com

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 variable à l'écran.

C'est si 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++, montrez-moi s'il vous plaît un exemple (que je peux compiler et tester)

Merci.


user362981: Merci pour votre aide. J'ai compilé le code que vous avez écrit sans problème, la fenêtre de console s’affiche et je ne peux saisir aucun caractère japonais dessus (avec IME). De plus, si Je change un mot de votre code ("bonjour") en un mot contenant des caractères japonais, il ne les affichera pas non plus.

Svisstack: Merci également pour votre aide. Mais lorsque je compile votre code, l'erreur suivante apparaît:

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*'
27
Dox

Vous allez obtenir beaucoup de réponses sur les personnages larges. Les caractères larges, en particulier wchar_tne correspondent pas à Unicode. Vous pouvez les utiliser (avec quelques pièges) pour stocker Unicode, tout comme vous pouvez utiliser un unsigned char. wchar_t dépend extrêmement du système. Pour citer le norme Unicode, version 5.2, chapitre 5:

Avec le type de caractères larges wchar_t, ANSI/ISO C fournit inclusion de caractères larges à largeur fixe. ANSI/ISO C laisse la sémantique du large jeu de caractères correspondant à l'implémentation spécifique, mais nécessite que les caractères du jeu d'exécution C portable correspondent à leurs équivalents caractères larges par extension zéro.

et cela

La largeur de wchar_t dépend du compilateur et peut ne pas dépasser 8 bits. Par conséquent, Les programmes devant être portables sur un compilateur C ou C++ ne doivent pas utiliser wchar_t pour stocker du texte Unicode. Le type wchar_t est destiné au stockage large .__ du compilateur. caractères, qui peuvent être des caractères Unicode dans certains compilateurs.

Donc, son implémentation est définie. Voici deux implémentations: Sous Linux, wchar_t a une largeur de 4 octets et représente le texte dans le codage UTF-32 (quelle que soit la langue actuelle). (BE ou LE, en fonction de votre système, quel que soit le système natif.) Cependant, Windows a un wchar_t large sur 2 octets et représente les unités de code UTF-16 avec eux. Complètement différent.

Un meilleur chemin: En savoir plus sur les paramètres régionaux, car vous devez le savoir. Par exemple, étant donné que ma configuration d’environnement est utilisée 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 à ce sujet. Il se contente de lire des caractères qui se présentent sous la forme UTF-8 parce que mon environnement est défini de cette manière. Je pourrais tout aussi facilement dire "bon sang, je suis en partie tchèque, utilisons ISO-8859-2": le programme reçoit tout à coup des contributions dans ISO-8859-2, mais comme il ne fait que le régurgiter, cela n'a pas d'importance. , le programme fonctionnera toujours correctement.

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

Sur de nombreux systèmes POSIX, les paramètres régionaux actuels sont généralement UTF-8, car ils offrent plusieurs avantages à l'utilisateur, mais cela n'est pas garanti. La sortie de UTF-8 vers stdout sera généralement correcte, mais pas toujours. Supposons que j'utilise ISO-8859-2: si vous envoyez stupidement un ISO-8859-1 "è" (0xE8) à mon terminal, un "è" (0xE8) apparaîtra. De même, si vous éditez un UTF-8 "è" (0xC3 0xA8), je verrai (ISO-8859-2) "è" (0xC3 0xA8). Cette correction de caractères incorrects a été appelée Mojibake .

Souvent, vous ne faites que brouiller les données et cela n'a pas beaucoup d'importance. Cela entre généralement en jeu lorsque vous devez sérialiser des données. (De nombreux protocoles Internet utilisent UTF-8 ou UTF-16, par exemple: si vous obtenez des données d'un terminal ISO-8859-2 ou d'un fichier texte codé sous Windows-1252, vous devez le convertir, ou vous envoyer Mojibake .)

Malheureusement, il s'agit de l'état du support Unicode, en C et C++. Vous devez vous rappeler que ces langages sont vraiment agnostiques envers le système et ne se lient pas à une manière particulière de le faire. Cela inclut les jeux de caractères. Cependant, il existe une multitude de bibliothèques permettant de traiter Unicode et d'autres jeux de caractères.

En fin de compte, ce n'est pas si compliqué que cela en réalité: vous devez savoir dans quel encodage vos données se trouvent et dans quel type d'encodage devrait figurer votre sortie. S'ils ne sont pas identiques, vous devez effectuer une conversion. Ceci s’applique que vous utilisiez std::cout ou std::wcout. Dans mes exemples, stdin ou std::cin et stdout/std::cout étaient parfois en UTF-8, parfois en ISO-8859-2.

37
Thanatos

Vous pouvez faire des choses simples avec la prise en charge générique des caractères étendus dans le système d'exploitation de votre choix, mais en général, C++ ne prend pas en charge la bonne prise en charge intégrée de l'unicode. Vous serez donc mieux à long terme en regardant quelque chose comme ICU .

1
Nick Bastin

Essayez de remplacer cout avec wcout, cin avec wcin et string avec wstring. Selon 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 y a d'autres moyens, mais c'est en quelque sorte la solution du "changement minimal".

1
EvanED
#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;
}
1
Svisstack

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

L'article ci-dessus est une lecture incontournable qui explique ce qu'est Unicode, mais il reste peu de questions. Oui, UNICODE a un code unique pour chaque caractère dans chaque langue. En outre, ils peuvent être codés et stockés en mémoire de manière potentiellement différente du code réel. De cette façon, nous pouvons économiser de la mémoire en utilisant par exemple le codage UTF-8, ce qui est excellent si le langage pris en charge est uniquement l'anglais et que la représentation de la mémoire est essentiellement la même que ASCII - ceci, bien sûr, connaissant le codage 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 stockez-vous un caractère/chaîne UNICODE dans un programme C++? Quel encodage utilisez-vous? La réponse est que vous n’utilisez aucun codage, mais vous stockez directement les points de code UNICODE dans une chaîne de caractères Unicode, tout comme vous stockez des caractères ASCII dans une chaîne ASCII. La question est de savoir quelle taille de caractères devez-vous utiliser, car les caractères UNICODE n'ont pas de taille fixe. La réponse simple est que vous choisissez une taille de caractère 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, ce qui peut créer une certaine confusion. Ne devrions-nous pas stocker des 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 alors qui ne contient que 2 octets, ce qui est clairement insuffisant pour stocker chaque point de code UNICODE?

La raison pour laquelle nous stockons le point de code de caractère UNICODE dans 2 octets dans Visual C++ est en fait exactement la même raison pour laquelle nous stockions le caractère ASCII (= anglais) dans un octet. À cette époque, nous ne pensions qu’en anglais, un octet suffisait. Nous pensons maintenant à la plupart des langues internationales mais pas à toutes. Nous utilisons donc 2 octets, ce qui est suffisant. Oui, c’est vrai que cette représentation ne nous permettra pas de représenter les points de code qui prennent 3 octets ou plus, mais nous ne nous en soucions pas encore car ces gens-là n’ont même pas encore acheté d’ordinateur. Oui, nous n'utilisons pas 3 ou 4 octets, car nous avons toujours une mémoire insuffisante. Pourquoi stocker l'octet supplémentaire 0(zero) avec chaque caractère alors que nous n'allons jamais l'utiliser (cette langue). Encore une fois, c’est exactement pour les mêmes raisons que ASCII stockait chaque caractère dans un octet. Pourquoi stocker un caractère dans deux octets ou plus, lorsque l’anglais peut être représenté dans un octet et dans une pièce afin d’épargner pour ces caractères spéciaux supplémentaires!

En théorie, 2 octets ne suffisent pas pour présenter chaque point de code Unicode, mais il suffit de contenir tout ce dont nous pouvons nous soucier pour le moment. Une vraie représentation de chaîne UNICODE pourrait stocker chaque caractère sur 4 octets, mais nous ne nous intéressons pas à ces langues.

Imaginez dans 1000 ans lorsque nous trouvons en abondance des extraterrestres sympathiques et que nous souhaitons communiquer avec eux en incorporant leurs innombrables langues. Une taille de caractère unicode unique passera peut-être à 8 octets pour prendre en charge tous les points de code. Cela ne signifie pas que nous devrions commencer à utiliser 8 octets pour chaque caractère unicode maintenant. La mémoire est une ressource limitée, nous attribuons ce dont nous avons besoin.

Puis-je gérer la chaîne UNICODE en tant que chaîne de style C? 

En C++, les chaînes ASCII peuvent toujours être traitées en C++, ce qui est assez courant en la récupérant par son pointeur char * auquel les fonctions C peuvent être appliquées. Toutefois, l’application des fonctions de chaîne de style C actuelles sur une chaîne UNICODE n’aura aucun sens, car elle pourrait contenir un seul octet NULL qui termine une chaîne C.

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

Ceci est facilité en C++ avec une classe spécialisée qui représente une chaîne UNICODE. Cette classe gère la complexité du tampon de chaîne unicode et fournit une interface simple. Cette classe décide également si chaque caractère de la chaîne unicode est de 2 octets ou plus - il s’agit des détails de la mise en oeuvre. Aujourd'hui, il peut utiliser wchar_t (2 octets), mais demain, il peut utiliser 4 octets pour chaque caractère afin de prendre en charge davantage 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 convient également de noter, et en particulier dans le traitement des chaînes de style C, qu’elles utilisent index pour parcourir ou rechercher une sous-chaîne dans une chaîne. Cet index dans la 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é.Qu'advient-il de la chaîne se terminant par l'octet NULL?.

Les chaînes UNICODE sont-elles toujours terminées par un octet NULL? Un seul octet NULL est-il suffisant pour terminer la chaîne? Ceci est une question d'implémentation, mais un octet NULL est toujours un point de code unicode et, comme tout autre point de code, il doit toujours avoir la même taille que tout autre (spécialement lorsqu'il n'y a pas d'encodage). Ainsi, le caractère NULL doit également être deux octets si l'implémentation de 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 autre.

Le débogueur Visual C++ affiche-t-il le 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 versions ultérieures prend en charge l'affichage du texte international dans la fenêtre de surveillance du débogueur (les polices et les modules de langue fournis sont bien sûr installés).

Résumé:.

C++ n'utilise aucun codage pour stocker les caractères Unicode, mais stocke directement les points de code UNICODE pour chaque caractère d'une chaîne. Il doit choisir une taille de caractère suffisamment grande pour contenir le plus grand caractère de langues souhaitables (en gros) et cette taille de caractère sera fixée et utilisée pour tous les caractères de la chaîne.

À l'heure actuelle, 2 octets suffisent 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 nous découvrions une nouvelle colonie d'espaces amis souhaitant communiquer avec eux, nous devrons affecter de nouveaux pionts de code Unicode à leur langue et utiliser une taille de caractère plus grande pour stocker ces chaînes.

Right now, 2 bytes are sufficient to represent most languages that we care about, this is why 2 bytes are used to represent code point. In future if a new friendly space colony was discovered that want to communicate with them, we will have to assign new unicode code pionts to their language and use larger character size to store those strings.

0
zar