Page suivante Page précédente - Table des matières

5. Comment faire pour que vos programmes comprennent Unicode

5.1 C/C++

Le type C 'char' est 8-bits et restera 8-bits parce qu'il désigne la plus petite unité de données adressable. Divers aménagements sont disponibles : 

Pour le traitement de texte normal

Le standard ISO/ANSI C contient, dans une correction qui fut ajoutée en 1995, un type de caractère codé sur 16 bits `wchar_t', un ensemble de fonctions comme celles contenues dans <string.h> et <ctype.h> (déclarées respectivement dans <wchar.h> et <wctype.h>), et un ensemble de fonctions de conversion entre `char *' et `wchar_t *' (déclarées dans <stdlib.h>).

Voici de bonnes réferences pour cette interface de programmation :

Avantages de cette interface de programmation :

  • C'est un standard non propriétaire.
  • Ces fonctions font ce qu'il faut, selon les locales de l'utilisateur. Tout ce qu'un programme doit faire est d'appeler setlocale(LC_ALL,"");.

Inconvénients de cette interface de programmation :

  • Certaines de ces fonctions ne sont pas "multithread-safe" parce qu'elles conservent un état interne caché entre les appels de fonction.
  • Il n'y a pas de type de donnée de première classe. Par conséquent cette API ne peut pas être utilisée raisonnablement pour tout ce qui nécessite plus d'une locale ou d'un jeu de caractères au même moment.
  • La plupart des systèmes d'exploitation ont un mauvais support de cette interface de programmation.

Notes concernant la portabilité

Une variable `wchar_t' peut être encodée en Unicode ou non. Ceci dépend de la plateforme et quelquefois aussi des locales. Une séquence multi-octets `wchar_t' peut être encodée en UTF-8 ou non selon la plateforme, et parfois selon les locales.

En détails, voici ce que la Single Unix specification dit à propos du type `wchar_t' :

Tous les codes de caractères 16 bits dans un processus donné consistent en un nombre égal de bits. Ceci en contraste avec les caractères, qui peuvent être constitués d'un nombre variable d'octets. L'octet ou la séquence d'octets qui représentent un caractère peuvent aussi être représentés comme un code de caractère 16 bits. Les codes de caractères 16 bits fournissent donc une taille uniforme pour manipuler les données textuelles. Un code de caractère 16 bits ayant tous les bits à 0 est un code de caractère 16 bits nul (null), et termine une chaîne. La valeur des caractères larges pour chaque membre du "Portable Character Set" (i.e ASCII) est égale quand il est utilisé en tant que seul caractère dans un caractère entier (integer) constant. Les codes de caractères 16 bits pour les autres caractères dépendent des locales et de l'implémentation. Les octets modificateurs d'état n'ont pas de représentation en code de caractère 16 bits.

Une conséquence notable est que dans des programmes portables vous ne devriez pas utiliser des caractères non-ASCII dans des chaînes littérales. Cela signifie que même si vous savez que les doubles guillemets ont les codes U+201C et U+201D, vous ne devriez pas écrire une chaîne littérale L"\u201cBonjour\u201d, dit il" ou "\xe2\x80\x9cBonjour\xe2\x80\x9d, dit il" dans des programmes C. Utilisez plutôt GNU gettext comme cela : gettext ("'Bonjour', dit il"), et créez une base de données de messages en.UTF-8.po qui traduit "'Bonjour' dit il" en "\u201cBonjour\u201d, dit il".

Voici une étude de la portabilité des aménagements ISO/ANSI C sur diverses implémentations d'Unix. La GNU glibc-2.2 les supportera tous, mais pour l'instant nous avons le tableau suivant.

GNU glibc-2.0.x, glibc-2.1.x

  • <wchar.h> et <wctype.h> existent.
  • Possède les fonctions wcs/mbs, mais pas fgetwc/fputwc/wprintf.
  • Pas de locales UTF-8.
  • mbrtowc retourne EILSEQ pour les octets >= 0x80.

Solaris 2.7

  • <wchar.h> et <wctype.h> existent.
  • Possède toutes les fonctions wcs/mbs, fgetwc/fputwc/wprintf.
  • Supporte les locales UTF-8 suivantes : en_US.UTF-8, de.UTF-8, es.UTF-8, fr.UTF-8, it.UTF-8, sv.UTF-8.
  • mbrtowc retourne EILSEQ pour les octets >= 0x80.

OSF/1 4.0d

  • <wchar.h> et <wctype.h> existent.
  • Possède toutes les fonctions wcs/mbs, fgetwc/fputwc/wprintf.
  • A en plus universal.utf8@ucs4 locale, voir "man 5 unicode".
  • mbrtowc ne connaît pas UTF-8.

Irix 6.5

  • <wchar.h> et <wctype.h> existent.
  • Possède les fonctions wcs/mbs et fgetwc/fputwc, mais pas wprintf.
  • N'a pas de locales multi-octets.
  • A seulement un simulacre de définition pour mbstate_t.
  • N'a pas mbrtowc.

HP-UX 11.00

  • <wchar.h> existe, mais pas <wctype.h>.
  • Possède les fonctions wcs/mbs et fgetwc/fputwc, mais pas wprintf.
  • A une locale C.utf8.
  • N'a pas mbstate_t.
  • N'a pas mbrtowc.

AIX 4.2

  • <wchar.h> existe, mais pas <wctype.h> - utilisez à la place <ctype.h> et <wchar.h>.
  • Possède les fonctions wcs/mbs et fgetwc/fputwc, mais pas wprintf.
  • Possède les locales UTF-8 suivantes : ET_EE.UTF-8, LT_LT.UTF-8, LV_LV.UTF-8, ZH_CN.UTF-8.
  • N'a pas mbstate_t.
  • N'a pas mbrtowc.

Par conséquent je recommande l'utilisation des fonctions redémarrables et multithread-safe wcsr/mbsr. Oubliez les systèmes qui ne les ont pas (Irix, HP-UX, Aix), et utilisez le plug-in qui permet d'utiliser des locales UTF-8, libutf8_plug.so (voir ci dessous) sur les systèmes qui vous permettent de compiler des programmes qui utilisent ces fonctions wcrs/mbsr (Linux, Solaris, OSF/1).

Un avis similaire, donné par Sun dans http://www.sun.com/software/white-papers/wp-unicode/, section "Internationalized Applications with Unicode", est :

Pour internationaliser correctement une application utilisez les indications suivantes :

  1. Évitez l'accès direct à Unicode. Ceci est la tâche de la couche d'internationalisation de la plateforme.
  2. Utilisez le modèles POSIX pour les interfaces multi-octets et à caractères 16 bits.
  3. Appelez seulement les fonctions de l'API que la couche d'internationalisation fournit pour la langue et les opération spécifiques à la culture.
  4. Restez indépendant de l'encodage.

Si, pour une raison quelconque, vous devez vraiment supposer que 'wchar_t' est Unicode dans un morceau de code (par exemple, si vous voulez faire un traitement spécial de certains caractères Unicode), vous devriez rendre ce bout de code conditionnel selon le résultat de is_locale_utf88(). Autrement vous allez mettre la pagaille dans le comportement de votre programme sur d'autres plateformes, ou si d'autres locales sont utilisées. La fonction is_locale_utf8() est déclarée dans utf8locale.h et définie dans utf8locale.c.

La bibliothèque libutf8

Une implémentation portable de l'API ISO/ANSI C, qui supporte les locales 8-bits et les locales UTF-8, peut être trouvée dans libutf8-0.5.2.tar.gz

Avantages :

  • Dès maintenant un support pour Unicode UTF-8 portable, même sur les systèmes d'exploitation dont le support des caractères multi-octets ne marche pas, ou qui n'ont pas du tout de support pour les caractères multi-octets/larges.
  • Le même binaire marche pour toutes les locales 8-bit et les locales UTF-8 supportées par le système.
  • Quand un système d'exploitation fournit un vrai support pour les caractères multi-octets, vous pouvez en tirer avantage simplement en recompilant sans l'option du compilateur -DHAVE_LIBUTF8.

La méthode Plan9

Le système d'exploitation Plan9, une variante d'Unix, utilise UTF-8 comme encodage dans toutes ses applications. Son type de caractère large est appelé 'Rune', pas 'wchar_'. Des parties ce ses bibliothèques, écrites par Rob Pike et Howard Trikey, sont disponibles à ftp://ftp.cdrom.com/pub/netlib/research/9libs/9libs-1.0.tar.gz. Une autre bibliothèque similaire, écrite par Alistair G. Crook, est à ftp://ftp.cdrom.com/pub/NetBSD/packages/distfiles/libutf-2.10.tar.gz.
En particulier, chacune de ces bibliothèques contient un moteur d'expressions rationnelles qui comprend l'UTF-8.

Désavantages de cette API :

  • UTF-8 est compilé dans la bibliothèque, pas optionnel. Les programmes compilés dans cet univers perdent le support des encodages 8-bits qui sont toujours utilisés fréquemment en Europe.

Pour les interfaces utilisateur graphiques

La bibliothèque QT-2.0 http://www.troll.no/ contient la classe QString qui est totalement Unicode. Vous pouvez utiliser les fonctions membres QString::utf8 et QString::fromUtf8 pour convertir depuis/vers un texte encodé en UTF-8. Les fonctions membres QString::ascii et QString::latin1 ne devraient plus être utilisées.

Pour la manipulation de texte avancée

Les bibliothèques mentionnées précédemment implémentent des versions des concepts ASCII qui comprennent Unicode. Voici des bibliothèques qui traitent des concepts Unicode, comme titlecase (Une troisième casse de lettres, différente des majuscules et des minuscules), la distinction entre la ponctuation et les symboles, la décomposition canonique, les classes combinables, le classement canonique et d'autres choses du même genre.

ucdata-1.9

La bibliothèque ucdata de Mark Leisher http://crl.nmsu.edu/~mleisher/ucdata.html s'occupe des propriétés des caractères, de la conversion de la casse, de la décomposition, des classes combinées.

ICU

Ce sont les classes IBM pour Unicode. http://www.alphaworks.ibm.com/tech/icu/. Une bibliothèque très détaillée comprenant des chaînes de caractères Unicode, des paquets de ressources, des formateurs de nombres, de date, d'heure et de messages, des assemblages, des assembleurs de messages et plus encore. Beaucoup de locales sont supportées. Cette bibliothèque est portable pour Unix et Win32, mais compilera sans intervention ("out of the box") seulement avec la libc6, pas la libc5.

libunicode

La librairie Unicode de GNOME http://cvs.gnome.org/lxr/source/libunicode/ de Tom Tromey entre autres. Elle couvre la conversion du jeu de caractères, les propriétés des caractères, la décomposition.

Pour la conversion

Deux bibliothèques de conversion qui supportent UTF-8 et un grand nombre de de jeux de caractères 8-bits, sont disponibles :

L'implémentation iconv d'Ulrich Drepper, contenue dans la GNU glibc-2.2.1

ftp://ftp.gnu.org/pub/gnu/glibc/glibc-2.1.1.tar.gz.

Avantages :

  • iconv est conforme au standard POSIX, les programmes qui l'utilisent pour la conversion depuis/vers UTF-8 tourneront aussi sous Solaris. Cependant le nom des jeux de caractères diffère entre les plateformes. Par exemple, "EUC-JP" sous glibc devient "eucJP" sous HP-UX. (Le nom INIA officiel pour ce jeu de caractères est "EUC-JP". Il s'agit donc clairement d'une déficience de HP-UX.)
  • Aucune bibliothèque supplémentaire n'est nécessaire.

librecode par François Pinard

ftp://ftp.gnu.org/pub/gnu/recode/recode-3.5.tar.gz.

Avantages :

  • Support pour la translittération, i.e conversion de caractères non-ASCII en séquences de caractères ASCII de façon à préserver la lisibilité pour les humains, même lorsqu'une transformation sans pertes est impossible.

Problèmes :

  • Cette API est non standard.

Les autre approches

libutf-8

libutf-8, de G.Adam.Stanislav <adam@whizkidtech.net> contient quelques fonctions pour la conversion depuis/vers des flux "FILE*". http://www.whizkidtech.net/i18n/libutf-8-1.0.tar.gz
Avantages :

  • Très petit.

Problèmes :

  • API non standard ;
  • UTF-8 est compilé dans la bibliothèque, pas optionnel. Les programmes compilés dans cet univers perdent le support des encodages 8-bits qui sont toujours utilisés fréquemment en Europe ;
  • L'installation n'est pas évidente : le Makefile doit être modifié. Pas d'auto-configuration.

5.2 Java

Java supporte Unicode en interne. Le type 'char' désigne un caractère Unicode, et la classe 'java.lang.String' désigne une chaîne de caractères construite à partir de caractères Unicode.

Java peut afficher n'importe quel caractère à travers son système de fenêtrage AWT, à condition que

  1. vous positionniez la propriété système "user.language" de façon appropriée.
  2. Les définitions de jeux de fontes /usr/lib/java/lib/font.properties.language soient appropriées, et
  3. Le fontes spécifiées dans ce fichier soient installées.
Par exemple, pour afficher du texte contenant des caractères japonais, vous devriez installer des fontes japonaise, et lancer "java -Duser.language=ja ...". Vous pouvez combiner les jeux de fontes : pour pouvoir afficher des caractères d'Europe de l'ouest, grecs et japonais simultanément, vous devriez créer une combinaison des fichiers "font.properties" (couvre ISO-8859-1), "font.properties.el" (couvre ISO-8859-7) et "font.properties.ja" dans un seul fichier. ??Ceci n'a pas été testé??

Les interfaces java.io.DataInput et java.io.DataOutput contiennent des méthodes appelées 'readUTF', et 'writeUTF' respectivement. Mais notez qu'elles n'utilisent pas UTF-8 ; elles utilisent un encodage UTF-8 modifié : le caractère NUL est encodé dans une séquence de deux octets 0xC0 et 0x80 à la place de 0x00, et un octet 0x00 est ajouté à la fin. Encodées de cette façon, les chaînes peuvent contenir des caractères NUL mais elles doivent néanmoins être préfixées par un champ de taille. Les fonctions C <string.h> comme strlen() et strcpy() peuvent être utilisées pour les manipuler.

5.3 Lisp

Le standard Lisp ordinaire détermine deux types de caractères : 'base-char' et 'character'. C'est à l 'implémentation d'ajouter un support Unicode ou non. Ce langage détermine aussi un mot-clef argument ':external-format' pour 'open' comme place naturelle pour spécifier un jeu de caractères ou un encodage.

Parmi les implémentation gratuites du lisp standard, seul CLISP http://clisp.cons.org/ supporte Unicode. Vous aurez besoin d'une version de CLISP datant de juillet 99 ou plus récente. ftp://clisp.cons.org/pub/lisp/clisp/source/clispsrc.tar.gz Les types "base-char" et "character" sont tous équivalents au 16-bits Unicode. L'encodage utilisé pour le fichier ou l'I/O socket/pipe peut être spécifié par l'argument ':external-format'. Les encodages utilisés pour les opérations d'entrée/sortie sur des ttys et l'encodage par défaut pour les I/O file/socket dépendent des locales.

Parmi les implémentations commerciales du Lisp standard, seule Eclipse http://www.elwood.com/eclipse/eclipse.htm supporte Unicode. Voir http://www.elwood.com/eclipse/char.htm Le type 'base-char' est équivalent à ISO-8859-1, et le type 'character' contient tous les caractères Unicode. L'encodage utilisé pour les entrées/sorties sur un fichier peut être défini à travers une combinaison des arguments de 'open' ':element-type' et :'external.format'.
Limitations : les fonctions d'attributs de caractères sont dépendantes des locales. Les sources et les fichiers de sources compilés ne peuvent pas contenir des chaînes Unicode littérales. L'implémentation commerciale du Lisp standard Allegro CL ne contient pas encore de support Unicode, mais Erik Naggum y travaille.

5.4 Ada95

Ada95 a été conçu pour supporter Unicode, et la bibliothèque standard Ada95 contient les types de données spéciaux ISO 10646-1 Wide_Character et Wide_String, ainsi que de nombreuses procédures et fonctions associées. Le compilateur Ada95 GNU (gnat-3.11 ou plus) supporte UTF-8 comme encodage externe des caractères 16 bits. Cela vous autorise à utiliser UTF-8 à la fois dans le code source et dans les entrées/sorties de l'application. Pour l'activer dans l'application, utilisez "WCEM=8" dans la chaîne FORM quand vous ouvrez un fichier, et utilisez l'option du compilateur "-gnatW8" si le code source est UTF-8. Pour plus de détails, voyez les manuels de référence GNAT et Ada95.


Page suivante Page précédente - Table des matières