Dumbbell's home

Unicode et UTF-8

Unicode (http://www.unicode.org/) définit un jeu de caractères le plus exhaustif possible, dans le but de régler les problèmes d'encodage de textes dans un contexte international. Par exemple, un document ou une interface qui supportent l'Unicode acceptent un texte en allemand comme en arabe, avec les contraintes du sens de la lecture que ça comporte.

Encodage et encapsulation

Actuellement, deux Unicode Character Set (UCS) sont définis :

UCS-4
chaque caractère étant encodé sur 4 octets, tout ceux définis dans Unicode sont pris en compte par ce charset.
UCS-2
ce charset est un sous-ensemble du précédent : seuls 2 octets sont utilisés ce qui permet d'écrire les 65 536 premiers caractères d'Unicode.

Pour faciliter l'intégration d'Unicode dans les systèmes actuels, on a recours aux Unicode Transformation Format (UTF) qui sont en quelque sorte des mécanismes d'encapsulation des caractères UCS. Étant donné le nombre de caractères, tout ne rentre pas dans un seul octet, comme ISO-8859-x, c'est pourquoi, on va devoir les encoder sur plusieurs octets, et chaque variante d'UTF apporte ses avantages et ses inconvénients. Sans rentrer dans les détails, en voici une courte liste :

UTF-8
Il permet d'encapsuler l'UCS-4 et chaque caractère fait de 1 à 4 octets. On le retrouve sur les plateformes Unix.
UTF-16
Il permet d'encapsuler l'UCS-2 et chaque caractère fait 2 octets. On distingue l'UTF-16LE (pour little endian) et l'UTF-16BE (pour big endian). On le retrouve sur les plateformes Microsoft Windows.
UTF-32
Il fonctionne sur le même principe que l'UTF-16 mais permet l'encapsulation des caractères UCS-4, donc chaque caractère fait 4 octets. Il est aussi affecté par les problématiques d'endianness.

Le fonctionnement de l'UTF-8

Au départ, UTF-8 devait remplir les conditions suivantes :

En tenant compte de tout ceci, le caractère UTF-8 a été défini comme ça :

Pour avoir un résumé plus graphique, ça donne ceci quand on regarde un caractère entier :

Conversion des caractères ISO-8859-1(5)

Unicode classe tous les caractères en plusieurs ensembles, selon la langue ou la région d'où ils viennent, leur utilisation, etc. Jusqu'à maintenant, pour la France, on utilisait l'encodage ISO-8859-1 qui apporte à l'US-ASCII les caractères accentués, la cédille ou encore les ligatures. Depuis l'euro, il a été remplacé par l'ISO-8859-15 dans lequel 8 caractères sont remplacés par d'autres, dont . Dans le classement Unicode, tous ces caractères se trouvent dans les catégories suivantes :

Pour la conversion des 2 premiers groupes, c'est tout simple. En prenant l'exemple de é, c'est plus clair. En UTF-8, il s'écrit 0xC3 0xA9, soit en binaire :

11000011 10101001

L'enveloppe UTF-8 est donc celle-ci :

110xxxxx 10xxxxxx

Elle nous indique que le caractère fait 2 octets (110xxxxx) ; les données restantes sont donc :

xxx00011 xx101001 -> 11101001

soit 0xE9 en hexadécimal qui est le caractère é en ISO-8859-1.

En C, le code suivant permet une conversion d'un caractère UCS-2 (précédemment extrait de sa capsule UTF-8) en un caractère ISO-8859-1 :

/*
 * Conversion d'un caractère UCS-2 en ISO-8859-1
 * ---------------------------------------------
 *
 * Les blocs Unicode qui nous intéressent sont :
 *   Basic Latin (US-ASCII)
 *   Latin-1 Supplement (Les caractères ISO-8859-1 après 0x80)
 * Parmis ces blocs, on ne converti que les caractères imprimables, les
 * caractères de contrôles (même s'ils produisent normalement un effet
 * comme l'espace insécable) sont ignorés.
 */

char
ucs2_to_iso8859_1(uint16_t uc)
{

        if ((uc >= 0x0020 && uc <= 0x007E) || /* Basic Latin */
            (uc >= 0x00A1 && uc <= 0x00FF))   /* Latin-1 Supplement */
                return (char)(uc & 0xFF);

        /* Les autres ne sont pas des caractères imprimables ou sont hors de
         * ISO-8859-1 */
        return '\0';
}

Ce code peut être utile avec SDL qui retourne un caractère UCS-2 après un évènement clavier.

Cette conversion simple fonctionne pour l'ISO-8859-1 mais pas pour l'ISO-8859-15, car ses nouveaux caractères sont distribués un peu partout dans les ensembles Unicode. Il faut donc fonctionner avec une table de correspondance.

Pour approfondir