À la découverte d'Unicode/Codage

Si chaque caractère peut être représenté à travers les points de code définis par le répertoire Unicode, l'informatique nécessite de leur attribuer en plus d'un numéro, une représentation binaire.

Si l'on pourrait s'attendre à ce qu'Unicode définisse un multiplet suffisamment large pour représenter chacun des points de code Unicode, pour des raisons historiques, la réalité est toute autre.

En pratique, il est souvent nécessaire de représenter ces points de code dans des unités définies préalablement à une époque où l'on pensait que huit ou seize bits seraient largement suffisants.

Pour être traités informatiquement, les points de code (qui définissent des caractères) doivent être représentables par une séquence de bits et/ou d'octets. Ceci est rendu possible au travers des systèmes de codage de caractères attribuant à chaque point de code une séquence d'unités de code.

En dehors de la Chine populaire, les codages de caractères les plus utilisés sont sans doute l'UTF-8 et l'UTF-16, dont nous donnons quelques notions ci-après.

Les autres codages existant sont les suivants : UTF-7; UTF-8; CESU-8; UCS-2; UTF-16; UTF-32 (UCS-4); UTF-EBCDIC; SCSU; Punycode; GB 18030;

Le répertoire Unicode peut contenir plus d'un million de caractères, ce qui est bien trop grand pour être codé par un seul octet (limité à des valeurs entre 0 et 255). Techniquement, il s'agit de coder les caractères Unicode sous forme de séquences de un à quatre codet de un octet chacun.

Par exemple le caractère "€" (euro) est le 8365e caractère du répertoire Unicode, son index, ou point de code est donc 8364 (on commence à compter à partir de 0).

La principale caractéristique d'UTF-8 est qu'elle est rétro-compatible avec la norme ASCII 7 bits, c'est-à-dire que tout caractère ASCII se code en UTF-8 sous forme d'un unique octet, identique au code ASCII. Par exemple "A" (A majuscule) a pour code ASCII 65 et se code en UTF-8 par l'octet 65. Cela ne s'applique qu'aux textes en anglais, car toutes les autres langues utilisent des lettres avec diacritiques, ou carrément un autre alphabet que latin. Chaque caractère dont le point de code est supérieur à 127 (caractère non ASCII) se code sur 2 à 4 octets. Le caractère "€" (euro) se code par exemple sur 3 octets : 226, 130, et 172.

Codage UTF-8

modifier

Le principe du codage UTF-8 est d'indiquer le nombre d'octets employés pour coder le caractère par une séquence de bits à 1 dans la partie des bits de poids fort du premier octet. La fin de la séquence de bits à 1 est marquée par un bit à 0. Les bits restant servent à coder les bits de poids fort du point de code du caractère.

  • Un bit de poids fort à 0 indique un codage sur un octet, pour les caractères ASCII de 0 à 127 codés sur les 7 bits restant.
  • Sinon, le nombre de bits à 1 indique le nombre total d'octets pour le caractère.
  • La séquence à un bit à 1 est réservée pour les octets supplémentaires de codage par groupe de 6 bits. Cela permet de repérer une position (adresse mémoire ou flux de lecture) située au milieu du codage d'un caractère.

Le tableau ci-dessous résume le codage UTF-8, où les lettres représentent les bits du point de code du caractère encodé.

Séquence de bits Points de code des caractères
0aaaaaaa U+000000-U+00007F
110aaaaa 10bbbbbb U+000080-U+0007FF
1110aaaa 10bbbbbb 10cccccc U+000800-U+00FFFF
11110aaa 10bbbbbb 10cccccc 10dddddd U+010000-U+10FFFF

Exemples

modifier
Exemples de codage UTF-8
Type Caractère Point de code
(hexadécimal)
Valeur scalaire Codage UTF-8
décimal binaire binaire hexadécimal
Contrôles [NUL] U+0000 0 0000000 00000000 00
[US] U+001F 31 0011111 00011111 1F
Texte [SP] U+0020 32 0100000 00100000 20
A U+0041 65 1000001 01000001 41
~ U+007E 126 1111110 01111110 7E
Contrôles [DEL] U+007F 127 1111111 01111111 7F
Texte [NBSP] U+00A0 160 00010 100000 11000010 10100000 C2 A0
é U+00E9 233 00011 101001 11000011 10101001 C3 A9
߿ U+07FF 2047 11111 111111 11011111 10111111 DF BF
U+0800 2048 0000 100000 000000 11100000 10100000 10000000 E0 A0 80
U+20AC 8 364 0010 000010 101100 11100010 10000010 10101100 E2 82 AC
U+D7FF 55 295 1101 011111 111111 11101101 10011111 10111111 ED 9F BF
Demi-codets U+D800 (néant) (codage interdit)
U+DFFF
Usage privé [] U+E000 57 344 1110 000000 000000 11101110 10000000 10000000 EE 80 80
[] U+F8FF 63 743 1111 100011 111111 11101111 10100011 10111111 EF A3 BF
Texte U+F900 63 744 1111 100100 000000 11101111 10100100 10000000 EF A4 80
U+FDCF 64 975 1111 110111 001111 11101111 10110111 10001111 EF B7 8F
Non-caractères U+FDD0 64 976 1111 110111 010000 11101111 10110111 10010000 EF B7 90
U+FDEF 65 007 1111 110111 101111 11101111 10110111 10101111 EF B7 AF

Variante : UTF-8 modifié

modifier

La variante « UTF-8 modifié » est un codage utilisé par Java basé sur UTF-8, mais codant les caractères au-delà de U+0x00FFFF sur 6 octets, c'est-à-dire 2 fois 3 octets pour les surrogates, car en Java le type caractère occupe 16 bits.

Séquence de bits Points de code des caractères
0aaaaaaa U+0000-U+007F
110aaaaa 10bbbbbb U+0080-U+07FF
1110aaaa 10bbbbbb 10cccccc U+0800-U+FFFF (incluant les paires U+D800-U+DBFF et U+DC00-U+DFFF)

De plus, le caractère nul (U+0000) est codé sur deux octets (C0 80), afin d'éviter l'octet nul, et de faciliter le repérage de fin de chaîne de caractère dans le code natif.

Variante : CESU-8

modifier

La variante CESU-8 est identique au « UTF-8 modifié » utilisé par Java, excepté le codage du caractère nul (U+0000).

UTF-16 est un codage Unicode où chaque caractère est codé sur une suite de un ou deux mots de 16 bits.

L'UTF-16 fait maintenant partie intégrante de la norme Unicode, qui dans son chapitre 3 Conformance la définit de façon très stricte.

L'UTF-16 n'est pas l'UCS-2 qui est le codage, plus simple, de chaque caractère sur deux octets. Ces deux normes sont pourtant appelées toutes les deux Unicode, car le codage est le même tant que l'on n'utilise pas les plages U+D800 à U+DFFF (en principe réservées) et les plages après U+FFFF (peu utilisées en occident).

L'UTF-16 est en particulier utilisé dans les environnements windows. Dans ce système, les API dites unicode utilisent ce standard. Il en va de même du système NTFS.

UTF-16 est le standard de chaînes de caractères utilisé par l'UEFI[1].

Description

modifier

Les points de code qui peuvent être représentés doivent être dans l’intervalle de validité U+0000 à U+10FFFF, et ne doivent pas être affectés à un non-caractère. Tous les caractères possibles dans Unicode possèdent de tels points de codes.

Tout point de code qui n’est pas un non-caractère, et dont la valeur peut être codée sur un seul codet de deux octets (16 bits), c’est-à-dire tout point de code U+0000 à U+D7FF et U+E000 à U+FFFD, est stocké sur un seul mot de 16 bits (la plage de non-caractères U+D800 à U+DFFF est donc exclue, c'est-à-dire les points de code dont les 5 bits de poids fort sont 11011).

Dans les autres cas, le caractère est un point de code d’un plan supplémentaire (donc entre U+10000 et U+10FFFD et dont les 16 bits de poids faible ne doivent pas égaler 0xFFFE ou 0xFFFF) ; il est alors stocké sur 2 mots (codets) successifs de 16 bits chacun, dont les valeurs correspondent aux points de codes réservés dans les demi-zones d’indirection allouées dans le plan multilingue de base des normes Unicode et ISO/CEI 10646 :

  • le premier mot aura les 6 bits de poids fort égaux à 110110 et sera donc compris dans l’intervalle [0xD800 .. 0xDBFF] (ici en numération hexadécimale) ; ce mot contiendra dans ses 10 bits de poids faible les 10 bits de poids fort de la différence (représentée sur 20 bits) entre le point de code à stocker et le premier point de code supplémentaire U+10000 ;
  • le second mot aura les 6 bits de poids fort égaux à 110111 et sera donc compris dans l’intervalle [0xDC00 .. 0xDFFF] (ici en numération hexadécimale) ; ce mot contiendra dans ses 10 bits de poids faible les 10 bits de poids faible du point de code à stocker.

Puis suivant le format de stockage des mots de 16 bits dans un flux ordonné d’octets, deux systèmes sont possibles pour le codage final :

Principe du codage UTF-16 en big endian (on représente ici les bits)
Numéro du caractère 00000000 000uuuuu xxxxxxyy yyyyyyyy
Codage UTF-16BE
(sur 2 octets)
  xxxxxxyy yyyyyyyy
(seulement si uuuuu = 00000)
Codage UTF-16BE
(sur 4 octets)
110110ww wwxxxxxx 110111yy yyyyyyyy
avec wwww = uuuuu - 1 (si uuuuu > 00000)
Principe du codage UTF-16 en little endian (on représente ici les bits)
Numéro du caractère 00000000 000uuuuu xxxxxxyy yyyyyyyy
Codage UTF-16LE
(sur 2 octets)
  yyyyyyyy xxxxxxyy
(seulement si uuuuu = 00000)
Codage UTF-16LE
(sur 4 octets)
wwxxxxxx 110110ww yyyyyyyy 110111yy
avec wwww = uuuuu - 1 (si uuuuu > 00000)

L’indication du type de codage utilisé (ordre des octets) peut être implicite pour le protocole utilisé, ou précisé explicitement par ce protocole (en indiquant par exemple les noms réservés "UTF-16BE" ou "UTF-16LE" dans un entête de charset MIME). Si le protocole ne permet pas de spécifier l’ordre des octets, et s’il permet l’une ou l’autre des alternatives, on pourra utiliser le codage UTF-16 du point de code valide U+FEFF comme indicateur en tête du flux de données (car un changement d’ordre de ses octets à la lecture du flux conduira à un point de code U+FFFE, valide dans Unicode mais affecté à un non-caractère et donc interdit dans ce cas dans tout flux UTF-16. Ce point de code ainsi représenté (appelé marque d’ordonnancement des octets, byte order mark en anglais, abrégé BOM) ne sera codé qu’au début du flux de données, et permet de savoir comment a été codé le flux :

1er octet 2ème octet Codage effectif
0xFE 0xFF big endian
0xFF 0xFE little endian

Si l’une des deux séquences de deux octets chacune est présente en tête de flux, le type de codage en est déduit et la séquence est retirée du flux : elle ne représente aucun caractère du texte stocké dans ce flux de données. Si aucune des deux séquences ne figure en tête du flux de données, la norme Unicode spécifie que le flux doit être décodé en big endian (UTF-16BE).

Le BOM peut également être présent en UTF-8 pour indiquer explicitement le codage.

Références

modifier
  1. http://x86asm.net/articles/uefi-programming-first-steps/