Le premier site francophone dédié au développement Pocket PC


Comprendre et utiliser Unicode
 
   

Unicode ou ANSI

J'ai très rapidement abordé le sujet Unicode dans mon article sur une application portable Win32-WinCE. Je vais approfondir le sujet, pour que vous compreniez bien ce qui se cache derrière tout ceci.

Historique

L'industrie américaine fût la première à diffuser des ordinateurs grand public. En 1980 Apple, Commodore et autre Atari n'existaient qu'en version Anglo-saxonne : Clavier QWERTY, notice en Anglais, pas d'accents ni de caractères "non US". La préoccupation des constructeurs n'était pas orienté grand public car ces machines s'adressaient à des passionnés qui parlaient Anglais par la force des choses. La norme d'échange du texte entre ces différents models est ASCII (American Standard Code for Information Interchange), une table de 128 caractères définie par l'American National Standards Institute (ANSI). Sur ces machines 8 bits un caractère est codé sur un octets, soit 256 possibilités, et nous avions des significations différents pour les caractères 128 à 255, car aucune norme n'existait. Quand des constructeurs ont proposés des machines spécifiques au pays (Amstrad CPC par exemple), les caractères 128 à 255 contenaient les lettres spécifiques au pays, les accents, les cédilles, les trémas... mais chaque constructeur suivait sa norme.
Avec DOS est apparu les pages de code OEM. Chaque pays avais sa page de code, c'est à dire une table différente pour les caractères 128 à 255. La page de code pour les USA est CP437 et contient tous les caractères pour dessiner des tableaux, pratique car les PC n'avais pas de carte graphique et ne fonctionnais qu'en mode texte. Par contre impossible d'avoir en même temps accents et tableaux car tout deux étaient dans des pages de codes différentes.
Windows ANSI, apparu avec Windows 3.0, reprend ce principe de page de code, avec les caractères 128 à 255 spécifiques pour chaque pays. Mais comme Windows travaille en mode graphique nous n'avions plus de soucis pour tracer des tableaux pour toutes les langues. Certain pays étaient toujours mis de coté, comme la Grèce qui comporte plus de 127 caractères spécifiques ou encore les pays Asiatiques.
La norme DBCS (Double Byte Character Sets) code certain caractère sur un octets, et d'autres sur deux octets, chaque langue ayant son propre jeux de caractère. Tous les pays du monde peuvent maintenant avoir une représentation informatique de leur alphabet. Par contre il est nécessaire de faire appel à des conversions pour passer d'une langue vers une autre, et la manipulation de chaînes contenant des caractères sur 1 octets ou 2 octets n'est pas triviale.
La norme Unicode code tous les caractères sur deux octets et tous les alphabets du monde son présents dans la table unique Unicode, qui fais 64ko (aujourd'hui 34168 caractères codifiés pour 24 langues http://www.unicode.org/)

Support d'Unicode

Windows NT supporte Unicode en natif, et quand on passe une chaîne ANSI à une fonction NT une conversion est faite avant d'appeler la même fonction Unicode.
Windows 95/98 ne supporte pas vraiment Unicode, sauf pour certaine fonction ou pour la couche OLE.
Windows CE ne fonctionne qu'en Unicode, pas de support de l'ANSI pour aucune fonction, sauf les fonctions de conversion ANSI<->Unicode.
Donc avec Windows CE, dés que l'on récupère des donnés du monde extérieur et que l'on désire les afficher, il faut faire une conversion Unicode.
eVb fonctionne nativement en Unicode, toute manipulation de chaînes de caractères est fait suivant cette norme.
eVc nécessite l'utilisation de quelques macros spécifiques pour manipuler correctement les chaînes Unicodes.

C++, Windows et Unicode

Si vous voulez capitaliser vos développements C++ pour réutiliser vos classes pour Windows 95, NT et CE, une série de macro permettent d'avoir une seul code qui fonctionne en ANSI ou Unicode.

  ANSI Unicode ANSI Unicode ANSI Unicode
Générique TCHAR LPTSTR TEXT("A")
Explicite CHAR WCHAR LPSTR LPWSTR "A" L"A"
Résolu char wchar_t char * wchar_t * 0x41 0x00 0x41 0x00 0x00 0x00

Si avant de faire #include <Windows.h> vous définissez les UNICODE et _UNICODE alors vous travaillez en Unicode (16 bits), sinon vous travaillez en ANSI (8 bits). Pour eVC c'est deux définitions sont toujours présentent, même si vous ne les mettez pas.

TCHAR est le type qui représente un caractère.
LPTSTR est le type qui représente un pointeur vers un caractère.
TEXT("") est une macro qui indique au compilateur de convertir la chaîne statique dans le bon mode.
wchar_t est un unsigned short, soit 2 octets.

Le zéro dans une chaîne, qui indique la fin de chaîne est toujours zéro en Unicode, mais un zéro sur 2 octets.

#define UNICODE
#define _UNICODE
#include <windows.h>

int WINAPI _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
  LPTSTR    sChaine = TEXT("Hello World") ;

  MessageBox (NULL, sChaine, TEXT("Message Windows"), MB_OK) ;
}

Conversion

Pour passer de l'Unicode vers l'ANSI et vice-versa il existe les API WideCharToMultiByte et MultiByteToWideChar.

ANSI->Unicode
Trouver la taille finale pour réserver le buffer : Size = MultiByteToWideChar (CP_ACP, 0, szAnsi, -1, NULL, 0) ;
Ensuite vous faites un LPWSTR wUnicode = new WCHAR[Size] puis vous effectuez la conversion
MultiByteToWideChar (CP_ACP, 0, szAnsi, -1, wUnicode, Size) ;

Unicode->ANSI
Trouver la taille finale pour réserver le buffer : Size = WideCharToMultiByte(CP_ACP, 0, wUnicode, -1, NULL, 0, NULL, &bTemp) ;
Ensuite vous faites un LPSTR szAnsi = new char[Size] puis vous effectuez la conversion
WideCharToMultiByte(CP_ACP, 0, wUnicode, -1, szAnsi, Size, NULL, &bTemp) ;

Attention les API Windows attendent une longueur de buffer en caractères et non pas en octets. Donc méfiance avec sizeof qui retourne le nombre d'octets. L'astuce est d'utiliser sizeof(chaine)/sizeof(TCHAR), par exemple:

TCHAR Buffer[512];
GetModuleFileName(NULL, Buffer, sizeof(Buffer)/sizeof(TCHAR));

Utiliser des fichiers textes en Unicode

François Léger ma fait remarqué que la gestion des fichiers textes Unicodes n'est pas naturelle. Effective il faut connaître l'astuce du marqueur de début de flux. Comme chaque caractère est codé sur un short, l'ordre de stockage mémoire est important. Quand votre processeur est little endian (Intel et toute machine Windows voir fichier Q102025) la valeur sur deux octets (un short) 0x1234 est stockée en mémoire 0x34 0x12. Quand votre machine est big endian elle est stockée 0x12 0x34.
Il existe donc dans la norme Unicode un marqueur de l'ordre du flux pour faire cette différence. C'est le BOM (Byte Order Mark) qui vaut 0xFEFF et sera représenté 0xFF 0xFE sur une machine little endian Windows et 0xFE 0xFF sur les machines big endian. Ce marqueur est indispensable pour qu'un fichier texte soit vu comme fichier Unicode.

Exemple pour générer un tel fichier:

   // Très important d'ouvrir le fichier en binaire "wb" 
   // si "wt" est utilisé le texte est convertit en ANSI
   FILE *file = _tfopen(TEXT("\\My Documents\\texte.txt"), TEXT("wb"));
#ifdef UNICODE
   WCHAR BOM = 0xFEFF ;
   fwrite (&BOM, sizeof(BOM), 1, file);
#endif
   _ftprintf(file, TEXT("Fichier texte multi mode\n"));
   fclose(file);

Conclusion

Unicode n'est pas compliqué mais c'est plus simple quand on sait ce qui se cache derrière tout ceci. Le support d'Unicode en natif dans Windows CE est une bonne chose, toutes les applications fonctionnent pour tous les pays. Par exemple si vous écrivez un programme qui possède une zone de saisie texte et bien il fonctionne aussi pour les Japonais et les Chinois. Je trouve cela drôlement pratique, et seul les PDA PocketPC sont capables de faire cela.

Rémi THOMAS

 
       
   
 
   
Copyright 2001-2004 - Tous droits réservés
 
   

iPAQ est un produit de COMPAQ.
Visual Tools est un produit de Microsoft Corporation.
Toutes les autres marques et produits présents dans ces pages sont la propriété exclusive de leurs sociétés respectives.