Licence CC BY-NC-SA

L’épopée des encodages

Dernière mise à jour :
Auteur :
Catégories :

Pour bien cerner les problèmes causés par les encodages et la situation actuelle, le mieux reste encore de retracer leur histoire.

Au commencement était l’ASCII

L’un des premiers encodages historiques est l’ASCII, soit l’American Standard Code for Information Interchange (en français, le « code américain normalisé pour l’échange d’informations »). C’est une norme étasunienne, inventée en 1961, qui avait pour but d’organiser le bazar informatique à l’échelle nationale. Ce n’est donc pas le premier codage de l’Histoire, mais il s’est imposé au point d’éclipser les précédents.1

Le terme « ASCII » est souvent employé incorrectement pour désigner des pages de code qui étendent l’ASCII ou qui en dérivent.

L’ASCII proprement dit utilise sept bits (et non huit !) et dispose donc de 128 (27) caractères uniquement, numérotés de 0 à 127. En effet, à l’époque, il était encore courant de regrouper les bits par sept et non par huit, ou alors de réserver le huitième bit pour vérifier l’intégrité des données (bit de parité).

Sans plus attendre, voici la table de l’ASCII. Pour trouver le code d’un caractère, il faut mettre bout à bout le chiffre hexadécimal de sa ligne et celui de sa colonne. Ainsi le caractère Z a pour code hexadécimal 0x5A (soit 90 en décimal).

-0

-1

-2

-3

-4

-5

-6

-7

-8

-9

-A

-B

-C

-D

-E

-F

0-

NUL

SOH

STX

ETX

EOT

ENQ

ACK

BEL

BS

HT

LF

VT

FF

CR

SO

SI

1-

DLE

DC1

DC2

DC3

DC4

NAK

SYN

ETB

CAN

EM

SUB

ESC

FS

GS

RS

US

2-

SP

!

"

#

$

%

&

'

(

)

\*

+

,

-

.

/

3-

0

1

2

3

4

5

6

7

8

9

:

;

<

=

>

?

4-

@

A

B

C

D

E

F

G

H

I

J

K

L

M

N

O

5-

P

Q

R

S

T

U

V

W

X

Y

Z

[

\

]

^

_

6-

`

a

b

c

d

e

f

g

h

i

j

k

l

m

n

o

7-

p

q

r

s

t

u

v

w

x

y

z

{

|

}

~

DEL

Table: Table des caractères ASCII

Un exemple (les valeurs sont en hexadécimal) :

1
2
texte :         "  A     v  a  i  n  c  r  e     s  a  n  s     p  e  r  i  l  .  .  .  "
codage ASCII :  22 41 20 76 61 69 6E 63 72 65 20 73 41 6E 73 41 70 65 72 69 6C 2E 2E 2E 22

Regardons un peu ce qu’il y a dans l’ASCII :

  • les vingt-six lettres de l’alphabet latin, en majuscules (0x41 – 0x5A) et en minuscules (0x61 – 0x7A), ainsi que les chiffres de 0 à 9 ;
  • divers signes de ponctuation, et d’autres symboles tels que les crochets, les accolades, l’arrobase… ;
  • des caractères blancs, c’est-à-dire l’espace mais aussi d’autres tels que le retour à la ligne (eh oui, c’est aussi un caractère). Ils sont marqués comme ça, en voici la liste :
    • SP (0x20) : espace (space) ;
    • HT (0x09) : tabulation horizontale (horizontal tab), le '\t' des programmeurs ;
    • VT (0x0B) : tabulation verticale (vertical tab), le '\v' des programmeurs ;
    • LF (0x0A) : nouvelle ligne (line feed), le '\n' des programmeurs ;
    • CR (0x0D) : retour chariot (carriage return), le '\r' des programmeurs ; marque la fin d’une ligne ;
    • FF (0x0C) : nouvelle page (form feed), le '\f' des programmeurs ;
  • des caractères de contrôle non imprimables (0x00 – 0x1F, et 0x7F), marqués comme ça ; en voici quelques-uns :
    • NUL (0x00) : caractère nul (null), le '\0' des programmeurs ; marque la fin d’une chaîne de caractères ;
    • des caractères servant à la communication entre programmes, périphériques ou machines ;
    • des caractères correspondant à des actions, comme BS (backspace, retour arrière), ESC (escape, échappement), CAN (cancel, annulation) ou BEL (bell, le « bip ! ») ;
    • d’autres encore.

Notons que LF et CR remplissent des rôles proches et sont tous deux utilisés pour marquer les nouvelles lignes :

  • sous Linux et Mac OS X, on utilise LF ;
  • sous Mac avant Mac OS X, on utilise CR ;
  • enfin, sous Windows, on utilise la séquence CRLF.

Un joyeux bazar ! En fait, les raisons sont historiques. Jadis, les ordinateurs n’avaient pas d’écran ; au lieu de s’afficher sur un écran, le texte produit par les programmes était imprimé sur du papier. Il y avait donc des caractères pour donner des instructions au chariot (la tête d’écriture) de l’imprimante : BS indiquait de reculer d’un caractère, CR (le « retour chariot ») indiquait de revenir au début de la ligne, LF indiquait de descendre d’une ligne… Pour produire une nouvelle ligne, il était donc nécessaire de combiner CR et LF.

Pour poursuivre la digression culturelle, les 128 caractères de l’ASCII n’ont pas été placés au hasard. Leurs codes ont été soigneusement étudiés. Par exemples :

  • À l’époque reculée où a été conçu l’ASCII, on communiquait encore parfois des données à l’ordinateur via des cartes perforées. Chaque emplacement codait un bit : 1 s’il y avait un trou, 0 sinon. La perforation était irréversible. Lorsqu’on n’avait pas encore spécifié de caractère, on laissait tous les emplacements intacts et le caractère valait donc 0 (tous les bits à 0). Ce caractère « non spécifié » se retrouve en ASCII avec NUL, le caractère nul, qui vaut 0. De même, lorsqu’on voulait effacer un caractère on perçait tous les emplacements, ce qui donnait 127 (tous les bits à 1) ; le caractère ASCII DEL (delete) correspond justement à cette suppression.
  • Les lettres majuscules sont séparées de leurs homologues minuscules par un intervalle de 32. Cela signifie qu’il suffit de modifier un bit (le sixième) pour passer des unes aux autres.

Pour plus de détails, consultez l’article de Wikipédia.

Comme on le voit, l’ASCII est simple. Il ne comporte que le strict nécessaire pour l’époque, pour utiliser un ordinateur… en anglais. Évidemment, les autres pays ont voulu employer leur propre langue.


  1. Parmi les dinosaures dont il subsiste encore des fossiles aujourd’hui, on peut citer EBCDIC

La révolte gronde

C’est pourquoi des extensions de l’ASCII sont apparues.

Certaines gardent la base ASCII et utilisent le huitième bit laissé libre afin d’avoir plus de caractères à disposition (deux fois plus, 256 = 28 au total). Ainsi, les codes de 0 à 127 correspondent encore aux caractères ASCII, tandis que les codes supérieurs (de 128 à 255, c’est-à-dire ceux avec le huitième bit valant 1) servent pour les nouveaux caractères.

D’autres restent sur sept bits, et modifient carrément les 128 caractères de l’ASCII pour leur propres besoins.

On imagine la pagaille que ça a été, lorsque chaque pays s’est mis à éditer sa propre page de code. Ça fonctionnait bien tant que les documents restaient dans la zone où leur propre encodage était en usage, mais les échanges internationaux étaient sujets à problèmes. Comme un même code signifiait des caractères différents d’une page à l’autre, le récepteur ne lisait pas la même chose que l’expéditeur. Par exemple, le symbole du dollar ($) aux États-Unis devenait celui de la livre (£) au Royaume-Uni : dégâts assurés !

Il y a bien eu des tentatives de stopper la multiplication des pages de code, mais elles ont été insuffisantes. L’exemple le plus significatif en est la norme ISO 646 de 1972. Elle a défini une page de code sur sept bits dérivant de l’ASCII, avec des caractères fixes (principalement les lettres, les chiffres et la ponctuation principale), les autres caractères étant laissés au choix. Cette norme, qui devait permettre une certaine compatibilité et un semblant d’ordre, a donné naissance à plusieurs pages de code nationales, mais elle n’était pas adaptée aux langues non latines et ne permettait pas de représenter assez de caractères.

ISO 8859 : huit bits pour les langues latines

Plus tard, une norme mieux pensée a fait son apparition : la norme ISO 8859. Cette fois, elle utilise huit bits donc 256 caractères au maximum. La norme ISO 8859 comporte en fait plusieurs « parties », c’est-à-dire des pages de code indépendantes, nommées ISO 8859-nn est le numéro de la partie. ISO 8859 a été pensée afin que les parties soient le plus largement compatibles entre elles. Ainsi, la plage inférieure (codes 0 à 127) est la base commune ASCII, et la plage supérieure (codes 128 à 255) est spécifique à chaque page de code ; elle est remplie en s’arrangeant pour qu’entre deux pages de code, des caractères en commun ou qui se ressemblent occupent le même code.

Cette norme a principalement servi aux langues latines d’Europe pour mettre au point une page de code commune. À elles seules, elles utilisent en tout dix parties d’ISO 8859, parfois appelées « latin-1 », « latin-2 », etc. ; la première est la principale et les suivantes en sont des évolutions visant à améliorer la prise en charge de certaines langues. En voici deux à connaître :

  • ISO 8859-1 (« latin-1 » ou « Occidental ») est une page de code très courante dans les pays latins et sur la Toile. C’est l’encodage utilisé initialement par les distributions Linux, mais elles migrent progressivement vers l’UTF-8 qu’on verra plus tard. Les systèmes Windows utilisent également un jeu dérivé, comme on le verra bientôt. Il a l’avantage de permettre d’écrire grosso modo toutes les langues latines, et ceci avec un octet par caractère seulement.
  • ISO 8859-15 (« latin-9 » ou « Occidental (euro) »), datant de 1998, introduit le signe de l’euro () et complète le support de quelques langues dont le français (avec Œ) en abandonnant des symboles peu usités (dont le mystérieux ¤ signifiant « monnaie »). Il est néanmoins moins utilisé que son grand frère ci-dessus.

-0

-1

-2

-3

-4

-5

-6

-7

-8

-9

-A

-B

-C

-D

-E

-F

0-

7-

ASCII

(sauf les caractères de contrôle, inutilisés)

8-

9-

non utilisé

A-

NBSP

¡

¢

£

¤/

¥

¦/Š

§

¨/š

©

ª

«

¬

-

®

¯

B-

°

±

²

³

´/Ž

µ

·

¸/ž

¹

º

»

¼/Œ

½/œ

¾/Ÿ

¿

C-

À

Á

Â

Ã

Ä

Å

Æ

Ç

È

É

Ê

Ë

Ì

Í

Î

Ï

D-

Ð

Ñ

Ò

Ó

Ô

Õ

Ö

×

Ø

Ù

Ú

Û

Ü

Ý

Þ

ß

E-

à

á

â

ã

ä

å

æ

ç

è

é

ê

ë

ì

í

î

ï

F-

ð

ñ

ò

ó

ô

õ

ö

÷

ø

ù

ú

û

ü

ý

þ

ÿ

Table: Table des caractères ISO 8859-1 (latin-1) et ISO 8859-15 (latin-9, caractères indiqués côte-à-côte si différents)

Le caractère NBSP (0xA0) est l’espace insécable (non-breaking space), c’est-à-dire une espace qui, contrairement à l’espace habituelle, ne « sépare » pas les mots. Ça veut dire que si on écrit « Bonjour ! » avec une espace insécable et que ça se retrouve à la fin d’une ligne d’affichage, « Bonjour » ne sera pas séparé du point d’exclamation (ils iront ensemble au début de la ligne suivante) contrairement à une espace simple. Ce caractère est surtout utilisé avec les signes de ponctuation (avant « ? », « ! », « ; », « : » et entre les guillemets “«” et “»”). Ce sont les espaces grises bizarres dans LibreOffice.

À nouveau un exemple, avec cette fois des caractères accentués, des guillemets et des espaces insécables (symbolisées par ▒ pour qu’on les voit), conformément aux règles typographiques :

1
2
texte :           «  ▒  À     v  a  i  n  c  r  e     s  a  n  s     p  é  r  i  l  .  .  .  ▒  »
codage latin-1 :  AB A0 C0 20 76 61 69 6E 63 72 65 20 73 41 6E 73 41 70 E9 72 69 6C 2E 2E 2E A0 BB

Les codes de 0x00 à 0x1F et 0x7F (les caractères de contrôle ASCII) et de 0x80 à 0x9F sont laissés inutilisés par la norme 8859. Pour les communications Internet, l’IANA a créé la norme ISO-8859 (attention, c’est le tiret qui change tout !), qui ajoute des caractères de contrôle à ces emplacements.

Windows s’est aussi basé sur latin-1 pour mettre au point son nouveau jeu de caractères occidental dans les années 1990. Windows-1252 (ou « CP1252 », parfois dit « ANSI » à titre officieux) est maintenant le jeu utilisé dans tous les systèmes Windows occidentaux, et remplace les anciennes pages de code (la page de code 437 pour les États-Unis, et la 850 pour l’Europe). Il étend latin-1 avec des caractères supplémentaires dans les codes libres (de 0x80 à 0x9F).

Écrire des « caractères spéciaux » sous Windows

Sous Windows, on peut insérer n’importe quel caractère de Windows-1252 (donc de latin-1 et d’ASCII) avec la combinaison Alt+codecode est le code décimal du caractère, précédé du chiffre zéro (sinon, le code est lu selon la vieille page de code du système — 850 ou 437). Les exemples les plus utiles pour le français :

  • Alt+0,1,9,2À
  • Alt+0,1,9,9Ç
  • Alt+0,2,0,1É
  • Alt+0,1,6,0 (ou Alt+2,5,5) → espace insécable
  • Alt+0,1,7,1 (ou Alt+1,7,4) → «
  • Alt+0,1,8,7 (ou Alt+1,7,5) → »

Écrire des « caractères spéciaux » sous Linux

Sous Linux, le clavier est plus complet et la touche AltGr (ou AltGr+Maj) combinée avec les autres donne accès à de nombreux caractères. Et si cela ne suffit pas, la touche Compose donne accès à encore plus. Je vous laisse chercher !


À partir de maintenant, plus d’excuse pour massacrer le français ! D’ailleurs, si vous êtes intéressé, je conseille la lecture de ce cours sur la typographie.

ISO 8859 a aussi été utilisé pour l’alphabet cyrillique (ISO 8859-5), l’arabe (ISO 8859-6), le grec (-7), l’hébreu (-8) et même le thaï (-11).

Il existe au total seize parties et il n’y en aura pas plus. En effet, on privilégie désormais le développement de l’Unicode.

ISO 2022 : du multi-octet pour les langues asiatiques

Pendant ce temps en Asie…

Les langues latines s’en sont relativement bien tirées. Elles ont réussi à ne pas dépasser la limite fatidique de l’octet, ce qui reste quand même le plus pratique à la fois pour les traitements et pour la consommation de mémoire. Mais les langues asiatiques — japonais, coréen et chinois — disposent de bien trop de caractères pour que tout tienne sur huit bits. Les encodages mis au point en Asie de l’Est étaient donc multi-octet. Certains utilisaient deux octets par caractère, ce qui offre 65 536 (216) codes différents.

Comme pour les langues latines, une norme a été mise au point pour les organiser, on l’appelle ISO 2022. Elle permet de jongler entre plusieurs jeux à l’aide de séquences d’échappement ; celles-ci indiquent le jeu utilisé dans ce qui suit.

On peut ainsi employer plusieurs jeux différents dans une même chaîne de caractères. En fait, la norme distingue une plage de codes pour les octets 0 à 127, une autre pour les octets 128 à 255, et permet d’assigner un jeu à chaque plage de façon indépendante. Ainsi, on peut même employer deux jeux simultanément (par exemples ASCII et kanjis japonais, ou sinogrammes et latin-1) ! Ou alors, en n’utilisant que la première plage, on peut faire passer du texte dans un vieux système à sept bits.

Les différents jeux sont totalement indépendants ; ils n’ont même pas à faire la même taille ! Certains requièrent un octet par caractère, d’autres deux.

En théorie, ISO 2022 est une norme universelle puisqu’elle permet de passer d’un encodage arbitraire à un autre. Elle contient d’ailleurs ISO 8859. Toutefois, elle a plusieurs défauts, dont sa complexité et sa nature d’encodage à état : non seulement les caractères n’ont pas une taille fixe, mais en plus il faut absolument lire le texte depuis le début pour interpréter les octets !

En pratique, les occidentaux n’ont donc pas adopté ISO 2022, qui n’a servi que pour le chinois (ISO 2022-CN), le coréen (ISO 2022-KR) et le japonais (ISO 2022-JP). Ces variantes restent encore répandues, surtout la japonaise, mais l’Unicode gagne du terrain.

Unicode, la solution ultime

Avec des normes comme ISO 8859 et ISO 2022, le problème d’interropérabilité était à moitié résolu. On avait certes normalisé les communications entre langues voisines, mais le système conservait des limites. Comment transmettre un texte (envoyer de l’arabe à un français p·ex·) si le jeu du destinataire ne contient pas tous les caractères nécessaires ? Comment écrire un texte avec des caractères de plusieurs alphabets (arabe et russe p·ex·), s’il n’existe pas de jeu qui les contienne simultanément ? ISO 2022 constituait une réponse à ces problèmes, mais on a vu qu’elle était loin d’être idéale.

La solution s’impose d’elle-même : créer un jeu de caractères universel, qui contienne tous les alphabets ! Cette idée toute simple a donné naissance à deux monuments : la norme ISO 10646 et Unicode.

logo d’Unicode

logo d’Unicode

Unicode est une norme développée par le consortium Unicode depuis 1990 (version 8.0 en juin 2015). Elle repose sur le jeu universel de caractères ou JUC (en anglais, UCS pour Universal Character Set), défini dans la norme parallèle ISO 10646.

Le jeu universel de caractères

L’objectif de ce répertoire est d’accueillir tous les caractères existants de toutes les langues du monde, actuelles ou passées. Un travail titanesque ! Concrètement, c’est un bête jeu de caractères, sauf que celui-ci offre pas moins de 17×216 = 1 114 112 codes. À l’origine, il prévoyait même 231 = 2 147 483 648 codes, mais il a vite été restreint : c’est déjà bien suffisant.

Devant une telle quantité, les polices d’écriture (qui n’ont rien à voir avec les encodages, il s’agit ici d’affichage des caractères et non de leur codage en mémoire) peinent à suivre. C’est ce qui explique les petits symboles du type � qu’on peut voir sur certaines pages (en regardant bien, vous lirez le code du caractère dans le carré). Généralement, les auteurs de polices se contentent de créer les glyphes des parties qui les intéressent, et parfois de copier les glyphes de référence pour le reste ; si on a vraiment besoin de rédiger une partition suivant la notation musicale grecque ancienne avec le JUC, on utilisera une police spécialisée…

En juin 2015, environ 11 % des codes sont déjà attribués à des caractères — en grande partie des idéogrammes asiatiques. Il reste encore de la place à revendre. Et pourtant, dans ces 11 %, on a tout mis ou presque : les caractères de tous les anciens jeux, tous les alphabets modernes, pléthore de symboles… Le JUC n’est peut-être pas la solution définitive et éternelle, mais on peut dormir sur nos deux oreilles pour les décennies à venir.

Par souci de compatibilité, le JUC reprend latin-1 (et donc l’ASCII) pour ses 256 premiers caractères.

Le jeu n’est pas définitif : de nouveaux caractères sont régulièrement créés et assignés à des codes encore libres. Cependant, les caractères déjà en place ne bougent plus.

Au fait, U+code est une notation officielle pour désigner le caractère de code hexadécimal donné. Par exemples, U+0041 est la lettre A et U+23CF est le symbole .

Écrire des caractères Unicode sous Linux

Sous Linux, dans certains cas (selon la méthode d’entrée utilisée), le raccourci Ctrl+Maj+U+code insère directement le caractère Unicode spécifié.

On peut voir Unicode comme une surcouche d’ISO 10646. ISO 10646 liste les caractères du jeu en leur assignant un nom et un code. Unicode leur ajoute des attributs et des relations. Unicode décrit aussi des algorithmes de traitement notamment pour les codages équivalents, les sens d’écriture, l’ordre alphabétique et les encodages pour transcrire le JUC.

Graphèmes vs points de code

J’avais évoqué en introduction la nuance entre graphème et caractère (qu’on appelle aussi point de code pour éviter les ambigüités). Cette distinction importe en Unicode, à cause d’un mécanisme appelé composition.

Pour écrire le graphème é (e accent aigu), je peux utiliser le caractère « précomposé » U+00E9 (hérité de latin-1), mais aussi la séquence U+0065, U+0301. U+0065 est le caractère de base (e, la lettre e) et U+0301 est une diacritique ( ́, l’accent aigu) ; les deux se composent pour former un seul graphème. Ce sont deux codages possibles du même graphème, ils doivent donc être considérés comme équivalents.

En pratique, la composition est peu utilisée pour les alphabets latins car on a hérité des caractères précomposés des jeux précédents ; et, de fait, les systèmes de rendu les gèrent assez mal. Testons le navigateur :

1
2
é (U+00E9)
é (U+0065, U+0301)

Chez moi, Firefox 45 affiche l’accent de la deuxième version trop à droite.

En revanche, une application intéressante de la composition, et qui montre qu’elle n’est pas restreinte aux diacritiques, est le hangeul, l’alphabet coréen. Comme les alphabets européens, les unités de base sont des lettres, mais au lieu d’être écrites une à une, elles sont regroupées par syllabes. Chaque syllabe forme un graphème composé de deux à six lettres. Par exemple, est la syllabe han, composée des trois lettres (h), (a) et (n). Unicode inclut les lettres du hangeul (dont 51 sont encore d’actualité et de nombreuses autres optionnelles), avec pour chacune une version isolée et plusieurs versions composables (selon la position dans la syllabe : initiale, médiane, finale). Cependant, Unicode inclut également les syllabes précomposées (utiles), évidemment beaucoup plus nombreuses (11 172).

1
2
한 (forme précomposée)
한 (forme décomposée)

Le hangeul est un fantasme de lingüistes, je vous laisse en apprendre plus sur l’article Wikipédia !

Ces particularités d’Unicode ont plusieurs conséquences :

  1. Le mécanisme de composition implique que, même si les points de code sont encodés avec une taille fixe — ce qui n’est pas le cas, comme on verra —, les graphèmes ont, eux, une taille variable !
  2. L’existence de caractères précomposés implique que le codage des graphèmes n’est pas unique. Unicode définit les équivalences entre codages, ainsi que des algorithmes pour calculer une forme normale.

Une application soigneuse doit prendre en compte tout ceci. Toutefois, je ne développerai pas plus sur ce sujet, cet article se focalisant surtout sur les encodages.

Un jeu, des encodages

Comme je l’ai dit, Unicode définit plusieurs encodages du JUC.

Des encodages ? Mais, je croyais qu’Unicode était un encodage ?

Unicode est essentiellement un jeu de caractères (un ensemble de caractères auxquels on attribue à chacun un point de code unique) et non un encodage (façon de représenter ce point de code en mémoire). C’est ici que la distinction prend tout son sens. Auparavant, les deux se confondaient puisque tous les jeux de caractères étaient associés à un encodage simple : vu que leurs codes tenaient sur un ou deux octets, on se contentait de les écrire tels quels en mémoire.

Or, les points de codes d’Unicode nécessitent beaucoup plus qu’un octet : il leur en faudrait quatre1 ! Cela voudrait dire que si l’on continuait à faire comme avant, on utiliserait quatre fois plus de mémoire qu’avec nos pages de code sur un octet, comme latin-1.

Ça ferait beaucoup de mémoire utilisée, voire gaspillée, puisqu’il y aurait plein d’octets nuls :

Codes

Encodage en UTF-32

Caractères disponibles dans cet intervalle

jusqu’à U+00FF (28-1)

00000000 00000000 00000000 bbbbbbbb

langues occidentales (latin-1)

jusqu’à U+FFFF (216-1)

00000000 00000000 bbbbbbbb bbbbbbbb

la quasi-totalité des alphabets actuels (BMP)

jusqu’à U+10FFFF (221-1)

00000000 000bbbbb bbbbbbbb bbbbbbbb

tous les caractères

Table: Consommation mémoire en UTF-32 (les codes sont en binaire, les b symbolisent les bits occupés)

Comme on le voit, il y aurait toujours au moins un octet nul, très souvent deux. Le cas extrême est celui d’un Occidental qui gaspille trois octets par caractère.

Cet encodage existe tout de même, il est nommé UTF-32, mais est rarement employé — sauf en interne par quelques programmes, car il reste plus facile à traiter que ses confrères que nous allons voir.

UTF-16

Pour faire des économies, on a donc mis au point des encodages plus futés. Tout d’abord, il y a l’UTF-16 (UCS transformation format, seize bits). Celui-ci a pour unité de base un doublet, c’est-à-dire deux octets (d’où le « 16 » dans son nom, comme le nombre de bits de l’unité de base). Les points de code inférieurs à 216 = 65 536 sont représentés tels quels sur deux octets, et les codes supérieurs sont représentés sur quatre octets via une transformation mathématique simple2.

Codes

Encodage en UTF-16

Caractères disponibles dans cet intervalle

jusqu’à U+FFFF (216-1)

bbbbbbbb bbbbbbbb

la quasi-totalité des alphabets actuels (BMP)

jusqu’à U+10FFFF (221-1)

bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb

tous les caractères

Table: Consommation mémoire en UTF-16 (les codes sont en binaire, les b symbolisent les bits occupés)

Les caractères de cet encodage ne font donc pas tous la même taille, ce qui complique un peu les traitements : si on stocke une chaîne UTF-16 dans un tableau de codes de deux octets, le ne caractère ne se trouve pas forcément dans la ne case. Ceci dit, étant donné que l’ensemble des points de codes inférieurs à 216 (le « BMP », pour Basic Multilingual Plane) contient déjà tous les alphabets actuels et symboles courants, il peut être suffisant de s’y restreindre (ce que fait Windows), et dans ce cas tous les caractères font bien deux octets.

De plus, l’UTF-16 (et l’UTF-32) fait surgir une difficulté technique supplémentaire : le boutisme (endianness). Ce terme mystique désigne l’ordre des octets d’un nombre multi-octet. Il en existe deux variantes principales : le gros boutisme (big-endian, BE) et le petit boutisme (little-endian, LE). Le boutisme dépend des machines. Or, en UTF-16, on lit les données par groupes de deux octets, donc le boutisme influe sur le résultat : si l’on lit du texte UTF-16 envoyé par quelqu’un utilisant un boutisme différent du sien, les deux octets de chaque caractère seront échangés, et c’est l’erreur assurée !

Pour pallier à cela, on utilise parfois un caractère spécial appelé BOM (byte order mark, marque d’ordonnancement des octets). l’IANA a aussi créé deux noms pour préciser le boutisme employé sur Internet : UTF-16BE et UTF-16LE.

Reprenons le texte d’exemple de tout à l’heure et encodons-le cette fois en UTF-16. En passant, remplaçons les trois caractères « point » (.) par le nouveau caractère « points de suspension » () qui est fait pour ça. Pour bien faire, ajoutons la BOM facultative :

1
2
texte :          [BOM] «____ ▒____ À____       v____ a____ i____ n____ c____ r____ e____       s____ a____ n____ s____       p____ é____ r____ i____ l____ …____ ▒____ »____
codage UTF-16 :  FE.FF 00.AB 00.A0 00.C0 00.20 00.76 00.61 00.69 00.6E 00.63 00.72 00.65 00.20 00.73 00.41 00.6E 00.73 00.41 00.70 00.E9 00.72 00.69 00.6C 20.26 00.A0 00.BB

Ici, la BOM indique que le texte est gros-boutiste (l’octet de poids fort est en premier). En petit-boutiste, on aurait ça :

1
2
texte :          [BOM] «____ ▒____ À____       v____ a____ i____ n____ c____ r____ e____       s____ a____ n____ s____       p____ é____ r____ i____ l____ …____ ▒____ »____
codage UTF-16 :  FF.FE AB.00 A0.00 C0.00 20.00 76.00 61.00 69.00 6E.00 63.00 72.00 65.00 20.00 73.00 41.00 6E.00 73.00 41.00 70.00 E9.00 72.00 69.00 6C.00 26.20 A0.00 BB.00

Si la chaîne contient une BOM, il suffit donc de lire ses deux premiers octets pour connaître son boutisme.

Remarquons que ce texte s’encode en UTF-16 exactement comme en latin-1, avec des valeurs sur deux octets au lieu d’un (donc un octet nul sur deux). Ici, on occupe donc deux fois plus de mémoire.

UTF-8

On a aussi inventé l’UTF-8. La taille des caractères codés est encore plus variable, et l’économie de mémoire plus grande. Comme son nom l’indique, l’unité de base est l’octet. Il code les premiers caractères (ceux de l’ASCII) sur un octet, les suivants sur deux, trois et jusqu’à quatre octets.

Codes

Encodage en UTF-8

Caractères disponibles dans cet intervalle

jusqu’à U+007F (27-1)

0bbbbbbb

latin de base (ASCII)

jusqu’à U+07FF (211-1)

110bbbbb 10bbbbbb

alphabets d’Europe et du Moyen-Orient 3

jusqu’à U+FFFF (216-1)

1110bbbb 10bbbbbb 10bbbbbb

la quasi-totalité des alphabets actuels (BMP)

jusqu’à U+10FFFF (221-1)

11110bbb 10bbbbbb 10bbbbbb 10bbbbbb

tous les caractères

Table: Consommation mémoire en UTF-8 (les codes sont en binaire, les b symbolisent les bits occupés)

On voit que l’UTF-8 est particulièrement intéressant pour les langues à alphabet latin6, qui bénéficient en plus de la compatibilité ASCII. Pour les alphabets grec, arabe ou cyrillique, UTF-8 ou UTF-16, c’est kif-kif ; on peut préférer UTF-16 pour sa plus grande constance, et l’économie qu’on réalise quand même sur quelques caractères moins fréquents. Pour tous les autres alphabets (dont les langues asiatiques), l’UTF-16 est préférable.

En plus d’être compatible avec l’ASCII, l’encodage UTF-8 a été conçu afin que certains algorithmes de traitement (comparaison lexicographique par exemple) soient réutilisables sans modification. Ainsi, de vieux programmes fonctionneront encore si on leur passe du texte en UTF-8 : magique !

Enfin, contrairement à l’UTF-16, on n’a pas de problème de boutisme puisqu’on lit les données octet par octet.

UTF-8 a de nombreux avantages (économie de mémoire, compatibilité, boutisme, résistance aux erreurs). Un défaut aussi : la grande variabilité de taille des caractères rend les traitements plus compliqués et nuit à leurs performances. Ses points forts ont cependant suffi à lui assurer le succès : il est l’encodage le plus répandu aujourd’hui. Il est utilisé pour les documents et les communications, et les systèmes d’exploitation (Mac et Linux) s’y mettent aussi.

Pour ne pas changer, voici notre texte d’exemple encodé en UTF-8 :

1
2
texte :         «____ ▒____ À____    v  a  i  n  c  r  e     s  a  n  s     p  é____ r  i  l  …_______ ▒____ »____
codage UTF-8 :  C2.AB C2.A0 C3.80 20 76 61 69 6E 63 72 65 20 73 41 6E 73 41 70 C3.A9 72 69 6C E2.80.A6 C2.A0 C2.BB

Constat flagrant, la consommation de mémoire est singulièrement réduite par rapport à l’UTF-16 ! On peut aussi observer la compatibilité ASCII, et les caractères non-ASCII s’étendant sur plusieurs octets (ici symbolisés par __). Tous les caractères de latin-1 utilisent au plus deux octets, mais le caractère en prend trois.


  1. Des petits malins me diront que trois octets suffiraient, mais en informatique on préfère les puissances de 2. 

  2. C’est d’ailleurs le codage UTF-16 qui a fixé la limite inhabituelle de 17×216 points de code (la limite précédente, 231, vient du codage UTF-8). 

  3. alphabets4 latin, API, grec, copte, cyrillique, arménien, hébreu, syriaque, arabe, thâna (utilisé aux Maldives), n’ko (utilisé en Afrique de l’Ouest) ; la carte se trouve ici

  4. Le saviez‐vous ? à l’exception peut-être du n’ko (création récente vaguement inspirée de l’arabe), tous ces alphabets5 sont les descendants encore en vie de l’alphabet phénicien, voire de son évolution en alphabet araméen. :-) 

  5. En fait, il faudrait dire « systèmes d’écriture » (l’arabe au moins n’étant pas un « alphabet »). Vous me pardonnez ? 

  6. qui, d’après Wikipédia, représenteraient tout de même 39 % de la population mondiale et 84 % des connexions à Internet 

Et aujourd’hui ?

Notre périple au fil des âges nous a ramené au temps présent. On a vu les encodages historiques, mais lesquels sont utilisés aujourd’hui, et dans quels contextes ?

Avant l’apparition d’Unicode, les systèmes d’exploitation fonctionnaient avec un système de pages de code régionales, c’est-à-dire qu’on utilisait des variantes d’encodage sur huit bits en fonction du pays. Depuis, ils ont migré vers Unicode en interne (y compris Windows, depuis XP, même si ça ne se répercute par forcément pour l’utilisateur).

  • Sous Windows, UTF-161 (little endian) est maintenant utilisé en interne et UTF-8 est également supporté, mais Unicode cohabite encore avec l’ancienne famille de pages de code. Ces dernières, incorrectement appelées « ANSI » et proches des ISO 8859, sont encore fréquemment employées. Je vous rappelle que celle pour nous autres Occidentaux est Windows-1252 et dérive de latin-1. Cette famille avait elle‐même remplacé l’antique famille dite « OEM » (pages de code CP437 et CP850 en Occident), dont l’usage perdure pourtant pour la console.
  • Sous Mac OS, l’encodage en vigueur en Occident était nommé MacRoman, mais depuis Mac OS X on utilise UTF-8.
  • Les distributions GNU/Linux utilisaient latin-1 par défaut, mais elles ont maintenant (presque ?) toutes migré à UTF-8.

Changer la page de code sous Windows

Sous Windows, on peut changer la page de code en vigueur depuis le Panneau de Configuration, « Options régionales et linguistiques », onglet « Avancé ». En fait, c’est la locale du système qu’on règle ainsi, ce pourquoi c’est présenté comme le choix de la langue par défaut des programmes.

Il est également possible de changer temporairement la page de code d’une console Windows grâce à la commande « CHCP » ; elle prend en argument le nº de la page à utiliser (CHCP 1252 pour Windows-1252) ; sans argument, elle affiche la page actuelle de la console (probablement 437 ou 850).

Changer la locale sous Linux

Sous un système unixoïde, c’est la locale qui détermine l’encodage en vigueur. On peut donc le changer à la volée via des variables d’environnement, à condition que la locale désirée soit installée. La vôtre est probablement fr_FR.UTF-8 (votre ordinateur vous parle en français de France avec l’encodage UTF-8).

Le système de locales sert aussi (surtout !) à adapter les programmes aux conventions propres à chaque langue et région :

1
2
3
4
$  date
Sun Aug 26 13:37:42 CET 2012
$  LC_ALL=fr_FR.UTF-8  date
dimanche 26 août 2012, 13:37:42 (UTC+0100)

Il est bien sûr possible, avec les bons outils, d’encoder un fichier de n’importe quelle façon, sous n’importe quel système d’exploitation. L’encodage d’un OS est simplement celui qu’il utilise en interne, donc aussi celui que les programmes ont tendance à utiliser, donc aussi celui des documents d’un utilisateur λ.

De plus, UTF-8 est maintenant l’encodage le plus utilisé sur la Toile, ce qui semble logique puisqu’il s’agit d’un réseau international mettant en contact toutes les langues. L’autre principal encodage sur Internet reste latin-1.

L’ASCII seul n’est plus employé, mais tous les encodages répandus conservent la base ASCII par compatibilité. Les trois principaux, cités ci-dessus, se basent même tous sur latin-1 (même si l’UTF-8 n’est pas compatible avec ce dernier puisque les codes supérieurs à 127 sont encodés par deux octets).

Si Unicode tend à remplacer progressivement tout le reste, il reste un endroit où survivent des encodages plus exotiques : l’informatique embarquée dans les petits appareils (lave-linges, calculettes, affichage des arrêts dans un bus…). En effet, ceux-ci n’ont parfois besoin que d’un jeu très restreint, et n’échangent pas de données avec un réseau.

Dans le même ordre d’idées, il existe une application qu’on utilise tous les jours et qui n’utilise pas Unicode : les SMS ! Ils emploient un encodage nommé GSM 03.38, un ensemble de pages de code sur sept bits vaguement compatibles avec l’ASCII. Toutefois, on peut utiliser UTF-16 à la place, ce que fait évidemment le trio Chine-Japon-Corée. Les téléphones actuels utilisent GSM 03.38 par défaut et basculent automatiquement en UTF-16 si jamais on écrit un caractère non supporté autrement. Un SMS étant limité à 140 octets, GSM 03.38 permet jusqu’à 160 caractères, alors qu’UTF-8 n’en permet que 70. On a beau s’entendre répéter que de nos jours la mémoire ne coûte rien et qu’on peut se permettre l’UTF-16, voilà un cas où l’on préférerait nettement faire des économies !


  1. ou plutôt sa restriction à deux octets, comme dit précédemment. 


Que retenir ?

  • Le principe du codage du texte en informatique : jeux de caractères et encodages.
  • Qu’un caractère n’est pas un octet. Ni même un nombre fixe d’octets. Que les programmeurs se le disent !
  • Les principaux encodages : ASCII, latin-1, UTF-8, pour n’en donner que trois.
  • Leurs usages actuels.

Il va de soi que je vous recommande d’utiliser l’UTF-8 exclusivement, partout. Ou éventuellement l’UTF-16 si vous utilisez un alphabet non latin.