UTF-8

Restez toujours informé : suivez-nous sur Google (☆)

Introduction

Unicode

Jeux de caractères


Équivalences normalisées


  • NFC (précomposée)
  • NFD (décomposée)
  • NFKC (compatibilité)
  • NFKD (compatibilité)

Propriétés et algorithmes


  • ISO 15924
  • Casse
  • Ordonnancement UCA
  • Texte bi-directionnel

Codage


Autres transformations


  • Punycode
  • GB 18030

Applications d'échanges de données


  • Courriel et Unicode
  • Unicode et HTML

UTF-8 (UCS transformation format 8 bits) est un format de codage de caractères. Il permet de gérer tous les caractères dits unicodes. Chaque caractère est codé sur un ou plusieurs points de code. Chaque point de code est codé sur une suite d’un à quatre octets. Il a été conçu pour être compatible avec certains logiciels originellement prévus pour traiter des caractères d’un seul octet.

Nota Bene: Le concept de point de code (en anglais code point) utilisé dans cet article est un concept défini en anglais pour la norme Unicode, et à la fois en anglais et en français dans la norme ISO/CEI 10646).

D'un point de vue technique, le format de transformation est défini pour tous les points de code ayant une valeur scalaire (qu’ils soient attribués ou non à des caractères ou des « non-caractères ») dans le répertoire universel (Universal Character Set, ou UCS), commun à la norme ISO/CEI 10646 et au standard Unicode (depuis sa version 1.1).

UTF-8 est apparu initialement dans une proposition de standard publiée en 1996 dans la RFC 2044 (« UTF-8, a transformation format of ISO 10646 »). Depuis lors, cette RFC a fait l’objet d’une normalisation par le comité technique de la norme ISO/CEI 10646 qui a publié en 1998 une première révision dans la RFC 2279. À peu près en même temps que le Consortium Unicode et l’ISO ont convenu de synchroniser leurs répertoires, le codage est aussi apparu dans le rapport technique 17 de la norme Unicode. D’autres restrictions ont été ajoutées et incorporées dans le standard Unicode en 2003, où il fait désormais partie intégrante de la norme dans son chapitre 3 « Conformité » (Conformance). À la même date ces restrictions ont été approuvées par l’Organisation internationale de normalisation (ISO) qui a publié la RFC 3629. Le codage UTF-8 est approuvé par l’Internet Engineering Task Force (IETF), qui suit ses évolutions dans le Standard Track BCP 63 ; il est également approuvé par le World Wide Web Consortium (W3C), et la plupart des organismes de normalisation nationaux.

L’IETF requiert maintenant qu’UTF-8 soit pris en charge par tous les nouveaux protocoles de communication d’Internet (publiés dans ses RFC numérotées) qui échangent du texte (les plus anciens protocoles n’ont toutefois pas été modifiés pour rendre ce support obligatoire, mais seulement étendus si possible, pour le supporter de façon optionnelle, si cela produit des incompatibilités ou introduit de nouveaux risques de sécurité : c’est le cas de protocoles Internet très utilisés comme DNS, HTTP, FTP, Telnet et de HTML dans ses versions initiales non normalisées par le W3C).

Description

Le numéro (valeur scalaire) de chaque point de code dans le jeu universel de caractères (UCS) est donné par la norme ISO/CEI 10646 qui assigne un point de code à chaque caractère valide, puis permet leur codage en leur attribuant une valeur scalaire identique au point de code ; cette norme est reprise dans le standard Unicode (qui utilise depuis la version 1.1 le même répertoire).

Tous les « points de code » (code points en anglais) de U+0000 à U+D7FF et de U+E000 à U+10FFFF sont représentables en UTF-8 (mêmes ceux attribués à des « non-caractères » (non character) et tous ceux qui ne sont pas encore attribués), et uniquement ceux-là. Les seuls points de codes valides dans l’espace de l’UCS et qui ne doivent pas être représentés dans UTF-8 sont ceux attribués aux « demi-codets » (surrogates en anglais), car ils ne sont pas représentables de façon bijective dans le codage UTF-16 et ne sont pas non plus par eux-mêmes des caractères : contrairement aux autres points de codes, les demi-codets n’ont donc pas de « valeur scalaire » (scalar value en anglais) définie.

Les caractères dont le point de code attribué (de U+0000 à U+007F) a une valeur scalaire identique de 0 à 127 sont codés sur un seul octet dont le bit de poids fort est toujours nul.

Les points de code de valeur scalaire supérieure à 127 sont codés sur plusieurs octets. Dans ce cas, les bits de poids fort du premier octet forment une suite de 1 de longueur égale au nombre d'octets utilisés pour coder le caractère, les octets suivants ayant 10 comme bits de poids fort.

Représentation binaire UTF-8Signification
0xxxxxxx1 octet codant 1 à 7 bits
110xxxxx 10xxxxxx2 octets codant 8 à 11 bits
1110xxxx 10xxxxxx 10xxxxxx3 octets codant 12 à 16 bits
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx4 octets codant 17 à 21 bits

Ce principe pourrait être étendu jusqu’à huit octets pour un seul point de code, mais UTF-8 pose la limite à quatre.

Le codage interdit la représentation des points de code réservés aux demi-codets (qui n’ont pas de valeur scalaire définie, afin de préserver la compatibilité avec UTF-16 qui ne permet pas non plus de les représenter). Mais il autorise cependant la représentation des points de code assignés à des non-caractères (alors même que leur présence est interdite dans un texte conforme).

Exemples

TypeCaractèrePoint de code

(hexadécimal)
Valeur scalaireCodage UTF-8
décimalbinairebinairehexadécimal
ContrôlesU+0000000000000000000000
U+001F310011111000111111F
TexteU+00203201000000010000020
AU+00416510000010100000141
~U+007E1261111110011111107E
ContrôlesU+007F1271111111011111117F
U+008012800010 00000011000010 10000000C2 80
U+009F15900010 01111111000010 10011111C2 9F
TexteU+00A016000010 10000011000010 10100000C2 A0
éU+00E923300011 10100111000011 10101001C3 A9
߿U+07FF204711111 11111111011111 10111111DF BF
U+080020480000 100000 00000011100000 10100000 10000000E0 A0 80
U+20AC8 3640010 000010 10110011100010 10000010 10101100E2 82 AC
U+D7FF55 2951101 011111 11111111101101 10011111 10111111ED 9F BF
Demi-codetsU+D800(néant)(codage interdit)
U+DFFF
Usage privéU+E00057 3441110 000000 00000011101110 10000000 10000000EE 80 80
U+F8FF63 7431111 100011 11111111101111 10100011 10111111EF A3 BF
TexteU+F90063 7441111 100100 00000011101111 10100100 10000000EF A4 80
U+FDCF64 9751111 110111 00111111101111 10110111 10001111EF B7 8F
Non-caractèresU+FDD064 9761111 110111 01000011101111 10110111 10010000EF B7 90
U+FDEF65 0071111 110111 10111111101111 10110111 10101111EF B7 AF
TexteU+FDF065 0081111 110111 11000011101111 10110111 10110000EF B7 B0
U+FFFD65 5331111 111111 11110111101111 10111111 10111101EF BF BD
Non-caractèresU+FFFE65 5341111 111111 11111011101111 10111111 10111110EF BF BE
U+FFFF65 5351111 111111 11111111101111 10111111 10111111EF BF BF
Texte𐀀U+1000065 536000 010000 000000 00000011110000 10010000 10000000 10000000F0 90 80 80
𝄞U+1D11E119 070000 011101 000100 01111011110000 10011101 10000100 10011110F0 9D 84 9E
🿽U+1FFFD131 069000 011111 111111 11110111110000 10011111 10111111 10111101F0 9F BF BD
Non-caractèresU+1FFFE131 070000 011111 111111 11111011110000 10011111 10111111 10111110F0 9F BF BE
U+1FFFF131 071000 011111 111111 11111111110000 10011111 10111111 10111111F0 9F BF BF
TexteU+20000131 072000 100000 000000 00000011110000 10100000 10000000 10000000F0 A0 80 80
U+2FFFD196 605000 101111 111111 11110111110000 10101111 10111111 10111101F0 AF BF BD
Non-caractèresU+2FFFE196 606000 101111 111111 11111011110000 10101111 10111111 10111110F0 AF BF BE
U+2FFFF196 607000 101111 111111 11111111110000 10101111 10111111 10111111F0 AF BF BF
...autres plans réservés...
SpéciauxU+E0000917 504011 100000 000000 00000011110011 10100000 10000000 10000000F3 A0 80 80
U+EFFFD983 037011 101111 111111 11110111110011 10101111 10111111 10111101F3 AF BF BD
Non-caractèresU+EFFFE983 038011 101111 111111 11111011110011 10101111 10111111 10111110F3 AF BF BE
U+EFFFF983 039011 101111 111111 11111111110011 10101111 10111111 10111111F3 AF BF BF
Usage privéU+F0000983 040011 110000 000000 00000011110011 10110000 10000000 10000000F3 B0 80 80
U+FFFFD1 048 573011 111111 111111 11110111110011 10111111 10111111 10111101F3 BF BF BD
Non-caractèresU+FFFFE1 048 574011 111111 111111 11111011110011 10111111 10111111 10111110F3 BF BF BE
U+FFFFF1 048 575011 111111 111111 11111111110011 10111111 10111111 10111111F3 BF BF BF
Usage privéU+1000001 048 576100 000000 000000 00000011110100 10000000 10000000 10000000F4 80 80 80
U+10FFFD1 114 109100 001111 111111 11110111110100 10001111 10111111 10111101F4 8F BF BD
Non-caractèresU+10FFFE1 114 110100 001111 111111 11111011110100 10111111 10111111 10111110F4 8F BF BE
U+10FFFF1 114 111100 001111 111111 11111111110100 10111111 10111111 10111111F4 8F BF BF

Caractéristiques

Dans toute chaîne de caractères codée en UTF-8, on remarque que :

  • tout octet de bit de poids fort nul désigne un unique « point de code » assigné à un caractère du répertoire de l’US-ASCII et codé sur ce seul octet, d’une valeur scalaire identique à celle du codet utilisé dans le codage US-ASCII ;
  • tout octet de bits de poids fort valant 11 est le premier octet d’une séquence unique représentant un « point de code » (assigné à un caractère ou un non-caractère) et codé sur plusieurs octets ;
  • tout octet de bits de poids fort valant 10 est un des octets suivants d’une séquence unique représentant un « point de code » (assigné à un caractère ou un non-caractère) et codé sur plusieurs octets ;
  • aucun octet ne peut prendre une valeur hexadécimale entre C0 et C1, ni entre F5 et FF (le plus haut point de code valide et assigné à un caractère représentable est U+10FFFD ; c’est un caractère à usage privé alloué dans le 17 plan valide).

Le plus grand point de code valide assignable à un caractère valide non privé est U+EFFFD dans le 15 plan (il n’est pas encore assigné mais peut le devenir dans l’avenir), mais le codage UTF-8 peut être utilisé aussi, de façon conforme aux normes, pour représenter n’importe caractère valide à usage privé (dans une des trois plages U+E000 à U+F8FF, U+F0000 à U+FFFFD, et U+100000 à U+10FFFD).

L’acceptation ou non des non-caractères ou des caractères d’usage privé est laissée aux applications ou protocoles de transport de texte. Cependant les non-caractères ne sont normalement pas acceptés dans des textes strictement conformes au standard Unicode où à la norme ISO/CEI 10646.

Certaines applications imposent des restrictions supplémentaires sur les points de code utilisables (par exemple, les standards HTML et XML interdisent, dans tout document conforme à ces spécifications, la présence de la plupart des caractères de contrôle entre U+0000 et U+001F et entre U+0080 et U+009F, en dehors des contrôles de saut de ligne et de la tabulation U+0009 considérés comme des caractères blancs, et interdisent aussi les non-caractères).

Tout point de code est toujours représenté par exactement la même séquence binaire, quelle que soit sa position relative dans le texte, et ces séquences sont autosynchronisées sur la position indivise des codets significatifs (ici les octets : on peut toujours savoir si un octet débute ou non une séquence binaire effective) ; ce codage autorise donc les algorithmes rapides de recherche de texte, tel que l’algorithme de Boyer-Moore.

Ce n’est pas toujours le cas des codages contextuels (qui utilisent généralement la compression de données, par exemple SCSU défini dans la note technique standard UTS#6 optionnelle complétant le standard Unicode) et qui peuvent nécessiter de lire le texte complètement depuis le début, ni des codages basés sur plus d’une seule variable d’état (ou qui incorporent des codes supplémentaires de redondance) ; au mieux certains de ces codages peuvent demander d’utiliser des algorithmes complexes de resynchronisation, basés souvent sur des heuristiques qui peuvent échouer ou conduire à de fausses interprétations si on ne lit pas le texte depuis le début (par exemple BOCU-1).

Principe et unicité du codage

Dans le tableau ci-dessus on voit que le symbole « € », est le caractère de numéro 8364 qui se code en binaire :

0010 000010 101100

qui sont les parties « non graissées » du codage binaire UTF-8.

log24096 = 12 et log28192 = 13 indiquent qu’avec 12 bits on peut coder les 4 096 premiers entiers (de 0 à 4 095), et qu’avec 13 bits on peut coder les 8192 premiers entiers (de 0 à 8 191).

Il faut donc 14 bits pour coder l’entier représentant le caractère « € ». Soit 2 tranches de 6 bits ; et ensuite en regardant dans le tableau "Définition du nombre d'octets utilisés", on voit que l'on dispose en plus de 4 bits ; ce qui nous fait 16 bits pour placer les 14 bits dont nous avons besoin.

Mais bien sûr si nous avions 3 × 6 + 3 = 21 bits nous pourrions là aussi placer nos 14 bits. Il est dit  : « Pour des raisons de sécurité, un programme qui décode des caractères au format UTF-8 ne doit pas accepter les séquences UTF-8 qui sont plus longues que nécessaires pour coder ces caractères. ». (« Il risquerait d'abuser un test de sous-chaîne, qui ne regarderait que les codages les plus courts ».)

Ainsi « € » se codera : 11100010 10000010 10101100,

mais ne se codera pas : 11110000 10000010 10000010 10101100.

Une telle forme, plus longue que nécessaire s'appelle en anglais overlong. De telles formes (initialement autorisées dans des spécifications anciennes avant qu’elles soient normalisées successivement par la RFC initiale publiée par le Consortium X/Open, puis parallèlement par la norme ISO 10646 et le standard Unicode) sont interdites et doivent être traitées comme invalides.

Types d’octets, séquences valides et décodage

Le codage est prédictif et permet toujours de retrouver la position du premier octet d'une séquence représentant un point de code, à partir de la valeur d’un octet quelconque et de la lecture d’un nombre limité d’octets voisins, dans les deux directions de lecture (ce sera toujours l’octet lui-même, ou le premier éligible dans un des 1 à 3 octets voisins).

  • Tout octet de continuation dans une séquence UTF-8 valide ne peut prendre que les valeurs hexadécimales 80 à BF ;
  • il ne peut exister qu’à la suite d’un octet de début de séquence (représentant un point de code), qui sera le dernier codé dans un des 1 à 3 octets précédents et qui n’est pas non plus un octet de continuation ;
  • le point de code suivant, s’il y en a un, ne peut commencer au maximum que dans les 1 à 3 octets suivants.
  • Le premier octet d’une séquence UTF-8 valide ne peut prendre que les valeurs hexadécimales 00 à 7F ou C2 à F4 :
  • le premier octet hexadécimal 00 à 7F d’une séquence n’est suivi d’aucun octet de continuation ;
  • le premier octet hexadécimal C2 à DF d’une séquence est toujours suivi d’un seul octet de continuation (chacun de valeur hexadécimale entre 80 et BF) ;
  • le premier octet hexadécimal E0 à EF d’une séquence est toujours suivi de deux octets de continuation (chacun de valeur hexadécimale entre 80 et BF) ;
  • cependant, si le premier octet d’une séquence prend la valeur hexadécimale ED, le premier octet de continuation est restreint à une valeur hexadécimale entre 80 et 9F ;
  • le premier octet hexadécimal F0 à F4 d’une séquence est toujours suivi de trois octets de continuation (chacun de valeur hexadécimale entre 80 et BF) ;
  • cependant, si le premier octet d’une séquence prend la valeur hexadécimale F4, le premier octet de continuation est restreint à une valeur hexadécimale entre 80 et 8F.

Séquences interdites

  • Les points de code sont toujours représentés par la séquence d’octets la plus courte possible.
  • Par conséquent, aucune séquence d’octets ne contient des octets initiaux de valeur hexadécimale C0 ou C1 dans un texte valide codé en UTF-8.
  • Les points de code allant de U+D800 à U+DFFF sont interdits.
  • Par conséquent, le premier octet de continuation d'une séquence qui commence par l’octet hexadécimal ED ne peut prendre aucune des valeurs hexadécimales A0 à BF.
  • De même que tout codage pouvant donner un point de code de valeur supérieure à U+10FFFF est interdit.
  • Par conséquent, le premier octet de continuation d'une séquence qui commence par l’octet hexadécimal F4 ne peut prendre aucune des valeurs hexadécimales 90 à BF.
  • Et aucune séquence d’octets ne contient des octets initiaux de valeur hexadécimale F5 à FF.

De telles séquences sont dites mal formées (ill-formed). (Voir la référence ci-dessus ou l’article Unicode).

En revanche, les points de code réservés (pas encore alloués à des caractères) sont autorisés (même si l’interprétation des caractères peut rester ambigüe) : il appartient aux applications de décider si ces caractères sont acceptables ou non, sachant que les mêmes applications continueront probablement à être utilisées alors que ces positions auront été assignées dans les normes Unicode et ISO 10646 à de nouveaux caractères parfaitement valides.

De même les autres points de code assignés de façon permanente aux autres « non-caractères » sont interdits dans les textes conformes à la norme ISO/CEI 10646 ou au standard Unicode : par exemple U+xFFFE à U+xFFFF (où x indique un numéro de page hexadécimal de 0 à 10). Mais ils restent encodables et décodables en tant que tels en UTF-8 (les non-caractères sont à disposition des applications qui peuvent en faire un usage au sein d’API internes, par exemple comme codes intermédiaires nécessaires à l’implémentation de certains traitements).

La restriction de l’espace de représentation aux seuls points de code inférieurs ou égaux à U+10FFFF (non compris les points de codes assignés aux demi-codets) n’a pas toujours été appliquée :

  • Cela n’a pas toujours été le cas dans la norme ISO/CEI 10646, qui prévoyait à l’origine de pouvoir coder un très grands nombre de plans possibles (l‘UCS-4 permettait un codage jusqu’à 31 bits), alors que Consortium Unicode (depuis la fusion du répertoire commun dans sa version 1.1) n’utilisait encore que le plan multilingue de base et n’avait pas encore envisagé de couvrir autant d’écritures qu’aujourd’hui.
  • L’introduction par Unicode du codage UTF-16 dans une annexe standard (quand il a admis que plus de 65536 caractères seraient rapidement nécessaires) a demandé l’allocation préalable par l’ISO/IEC 10646 d’un bloc de points de codes pour des « demi-codets » qui étaient considérés au début par l’ISO/IEC 10646 comme des caractères spéciaux (une concession faite à Unicode alors que l’UCS-4 avait été créé comme un espace de codage linéaire où tous les points de code avaient une valeur scalaire), alors qu’Unicode n’utilisait encore que le sous-espace UCS-2 et pas l’espace UCS-4 complet.
  • Pour éviter des problèmes d’interopérabilité avec les autres applications (non Unicode) basées sur UCS-2, une première révision de l’UTF-8 a été publiée par l’ISO en 1998, mentionnant que ces demi-codets n’avaient donc pas de valeur scalaire définie et qu’aucun point de code assignés aux « demi-codets » dans les deux blocs successifs alloués ne devait pas être encodés en UTF-8.
  • Mais selon l’accord final intervenu entre le comité technique du Consortium Unicode et le celui en charge de la norme ISO/CEI 10646, toute utilisation de plus de 17 plans a été proscrite, afin d’assurer l’interopérabilité totale avec le codage UTF-16 défini par Unicode, un codage déjà massivement déployé dans les systèmes d’exploitation (par exemple Microsoft Windows), ou sous-systèmes (par exemple OpenType), ou encore dans de nombreux langages de programmation qui en dépendent pour leur interopérabilité (dont certains issus de normes nationales ou internationales, tels que les langages C et C++ qui ont eu des difficultés à supporter le répertoire universel).
  • En effet, après une vingtaine d’années d’efforts pour la définition de l’UCS pour toutes les écritures du monde, des règles plus strictes ont été établies pour limiter les caractères encodables selon un modèle assurant une compatibilité ascendante mais aussi de meilleures pratiques de codage. Pratiquement toutes les écritures modernes du monde ont été codées, et on dispose d’estimations fiables de l’ordre de grandeur sur la quantité de caractères nécessaires pour le support des autres écritures, ainsi que sur les besoins de codage pour de nouveaux caractères.
  • La croissance initiale très forte des allocations dans l’UCS (ou des caractères restant encore à coder) s’est fortement ralentie, et seulement 5 des 17 plans sont utilisés en fin 2009 (mais deux seulement ont un taux de remplissage significatif : le plan de base multilingue, pratiquement plein, et le plan idéographique supplémentaire ; c’est sur le plan multilingue supplémentaire que se concentrent la majorité des autres écritures anciennes restant à encoder, ou des nouveaux ensembles de symboles et caractères de notation technique).
  • Le rythme de croissance des allocations dans l’UCS pour la norme ISO/IEC 10646 ne permet pas d’envisager sa saturation avant un terme dépassant de très loin le cycle de vie des normes internationales (et encore plus celui des standards industriels comme Unicode). À ce terme trop lointiain, il est tout à fait possible que UTF-16 soit devenu obsolète depuis fort longtemps, ou qu’une nouvelle norme de codification ait vu le jour et ait été massivement déployée (et que les outils de conversion automatique auront aussi été normalisés et déployés). Rien ne justifie encore de maintenir une extension possible non nécessaire au besoin immédiat d'interopérabilité des normes et standards actuels ou des futures normes envisagées.
  • Un ou deux autres plans seulement sont envisagés pour les écritures sinographiques, anciennes écritures cunéiformes ou hiéroglyphiques, et éventuellement un autre plan pour des collections de symboles et pictogrammes nécessaires à l’interopérabilité de certaines applications modernes (par exemple les emojis des messageries et services interactifs est-asiatiques, ou des symboles nécessaires à des normes internationales de signalisation ou de sécurité).
  • Les « groupes » supplémentaires d’usage privé à la fin de l’UCS-4, ainsi que les « plans » supplémentaires d’usage privé dans l’UCS-4 à la fin du groupe 0, qui avaient été envisagés par l’ISO depuis le début de ses travaux de normalisation, ont été abandonnés pour ne garder, parmi les 17 premiers plans du premier groupe, que les deux derniers plans à cet usage privé (en plus du bloc d’usage privé U+E000 à U+F8FF déjà alloué dans le plan multilingue de base), ce qui s’avère suffisant pour toutes les applications.
  • Cela a fait l‘objet de la révision en 2003 de la RFC publiée par le comité technique de l’ISO définissant le codage UTF-8 pour la norme ISO/CEI 10646, et simultanément d’une mise à jour de l’annexe standard au standard Unicode (une annexe standard qui a, depuis, été intégrée au standard lui-même).
  • Depuis ces mises à jour de 2003, le codage UCS-4 défini par la norme ISO/CEI 10646 est devenu en pratique équivalent à UTF-32 (défini dans la norme Unicode qui adjoint des propriétés supplémentaires mais sans différence de codage). Et la dernière RFC publiée par l’ISO et approuvée par l’IETF en 2003 fait d’ailleurs maintenant une référence normative à la définition de l’UTF-8 publiée conjointement avec (ou dans) le standard Unicode.

Avantages

Universalité :

Ce codage permet de représenter les milliers de caractères du répertoire universel, commun à la norme ISO/CEI 10646 et au standard Unicode (du moins depuis sa version 1.1).

Compatibilité avec US-ASCII :

Un texte en US-ASCII est codé identiquement en UTF-8.

Interopérabilité :

Du fait qu’un caractère est découpé en une suite d’octets (et non en mots de plusieurs octets), il n'y a pas de problème d’endianness (ou « boutisme »).

  • Ce problème apparaît avec les codages UTF-16 et UTF-32 par exemple, si on ne les utilise pas avec un marqueur d’ordonnancement (appelé BOM pour Byte Order Mark) codé en début de fichier à l’aide du caractère U+FEFF, qui était auparavant destiné à un autre usage (ZWNBSP pour zero-width non-breaking space, une fonction d’agglutination de mots à afficher sans espace séparatrice ni césure que remplit aujourd’hui le caractère ZWWJ pour zero-width word-joiner). En revanche, les codages dérivés UTF-16BE, UTF-16LE, UTF-32BE et UTF-32LE sont conçus avec un ordonnancement précis ne nécessitant l’emploi d’aucun BOM.
  • Pour différentes raisons de compatibilité (notamment via des processus de transcodage), il est cependant resté admis qu’un BOM (U+FEFF), non absolument nécessaire, puisse encore être codé en tête d’un fichier UTF-8 (leur interprétation reste celle du caractère ZWNBSP, même si de nombreux protocoles ont choisi d’ignorer et filtrer silencieusement ce caractère puisqu’il ne sert plus qu’à cet usage et que son ancienne fonction, quand elle reste nécessaire à l’interprétation du texte lui-même, est désormais transférée sur un autre caractère codé exprès).

Efficacité :

Pour la plupart des langues à écriture latine, les fichiers de données numériques ou les codes sources de programmes, ou de nombreux protocoles textuels de communication (comme FTP, HTTP ou MIME), qui utilisent abondamment (voire parfois exclusivement dans certaines parties) les caractères US-ASCII, UTF-8 nécessite moins d’octets que l’UTF-16 ou l’UTF-32.

Réutilisabilité :

De nombreuses techniques de programmation informatique valables avec les caractères uniformément codés sur un octet le restent avec UTF-8, notamment :

  • la manière de repérer la fin d’une chaîne de caractères C, car tout octet binaire 00000000 trouvé dans une chaîne de caractères codés en UTF-8 est toujours le caractère nul (en revanche il est alors impossible de représenter le caractère NUL lui-même comme membre de la chaîne de caractères, à moins que l’information de longueur effective du texte codé soit stockée ou transportée ailleurs de celui-ci, auquel cas cet octet sera interprété comme tel au sein même des chaînes codées en UTF-8).
  • la manière de trouver une sous-chaîne est identique.

Fiabilité :

Il s’agit d’un codage auto-synchronisant (en lisant un seul octet on sait si c’est le premier d’un caractère ou non).

  • Il est possible, depuis n’importe quelle position dans un texte codé, de remonter au premier octet de la séquence en lisant une toute petite quantité d’octets antérieurs, soit au maximum 3 octets, ou de trouver facilement le début de la séquence suivante, là encore en ne sautant qu’au maximum 3 octets) ;
  • Une séquence décrivant un caractère n’apparaît jamais dans une séquence plus longue décrivant un autre caractère (comme c’est le cas de Shift-JIS).
  • Il n’existe pas de code « d'échappement » changeant l’interprétation (comme caractères) de la suite d’une séquence d’octets.

Inconvénients

Taille variable :

Les caractères sont représentés en UTF-8 par des séquences d’octets de taille variable, ce qui rend certaines opérations sur les chaînes de caractères plus compliquées : le calcul du nombre de caractères ; le positionnement à une distance donnée (exprimée en caractères) dans un fichier texte et en règle générale toute opération nécessitant l’accès au caractère de position N dans une chaîne.

Efficacité :

Pour les langues utilisant beaucoup de caractères extérieurs à US-ASCII, UTF-8 occupe sensiblement plus d’espace.

Par exemple, les idéogrammes courants employés dans les textes de langues asiatiques comme le chinois, le coréen ou le japonais (kanji, par exemple) utilisent 3 octets en UTF-8 contre 2 octets en UTF-16.

De manière générale, les écritures employant beaucoup de caractères de valeur égale ou supérieure à U+0800 occupent plus de mémoire que s’ils étaient codés avec UTF-16 (UTF-32 sera plus efficace uniquement pour les textes utilisant majoritairement des écritures anciennes ou rares codées hors du plan multilingue de base, c’est-à-dire à partir de U+100000, mais il peut aussi s’avérer utile localement dans certains traitements pour simplifier les algorithmes, car les caractères y ont toujours une taille fixe, la conversion des données d’entrée ou de sortie depuis ou vers UTF-8 ou UTF-16 étant triviale).

Séquences invalides :

Par son système de codage, il était éventuellement possible de représenter un code de différentes manières en UTF-8, ce qui pouvait poser un problème de sécurité : un programme mal écrit peut accepter un certain nombre de représentations UTF-8, normalement invalides selon la RFC 3629 et dans les spécifications (maintenant équivalentes entre elles) publiées par l’ISO 10646 et Unicode ; mais ce n’était pas le cas selon la spécification originale, qui permettait de les convertir comme un seul et même caractère.

De fait, un logiciel détectant certaines chaînes de caractères (pour prévenir les injections SQL, par exemple) pouvait échouer dans sa tâche (ce n’est plus le cas si la conformité du codage avec la définition stricte et normalisée d’UTF-8 est vérifiée avant toute chose).

Prenons un exemple tiré d'un cas réel de virus attaquant des serveurs HTTP du Web en 2001 ((en)[2] [3] [4]). Une séquence à détecter pourrait être « /../ » représentée en ASCII (a fortiori en UTF-8) par les octets « 2F 2E 2E 2F » en notation hexadécimale.

Cependant, une manière malformée de coder cette chaîne en UTF-8 serait « 2F **C0 AE** 2E 2F », appelée aussi en anglais overlong form (forme superlongue). Si le logiciel n’est pas soigneusement écrit pour rejeter cette chaîne, en la mettant par exemple sous forme canonique, une brèche potentielle de sécurité est ouverte. Cette attaque est appelée directory traversal.

Les logiciels acceptant du texte codé en UTF-8 ont été blindés pour rejeter systématiquement ces formes longues car non conformes à la norme : soit le texte entier est rejeté ; mais parfois les séquences invalides sont remplacées par un code de substitution (généralement U+FFFD si l’application accepte et traite ce caractère normalement, parfois un point d’interrogation qui peut poser d’autres problèmes) ; moins souvent, ces séquences interdites sont éliminées silencieusement (ce qui est très peu recommandé).

Histoire

UTF-8 a été inventé par Kenneth Thompson lors d'un dîner avec Rob Pike aux alentours de septembre 1992. Il a été immédiatement utilisé dans le système d'exploitation Plan 9 sur lequel ils travaillaient. Une contrainte à résoudre était de coder les caractères nul et '/' comme en ASCII et qu'aucun octet codant un autre caractère n'ait le même code. Ainsi les systèmes d'exploitation UNIX pouvaient continuer à rechercher ces deux caractères dans une chaîne sans adaptation logicielle.

Restrictions successives

Le codage original était alors appelé FSS-UTF (File-System Safe Unicode Transform Format) et était destiné à remplacer le codage multi-octets UTF-1 initialement proposé par l'ISO 1646. Ce codage initialement permissif, permettait plusieurs représentations binaires pour le même caractère (cela a été interdit dans la version normalisée dans la RFC publiée par le Consortium X/Open, et approuvé par Kenneth Thompson).

De plus il pouvait (dans une version préliminaire non retenue) coder tous les caractères dont la valeur de point de code comprenait jusqu'à 32 bits en définissant un huitième type d'octet (dans des séquences comprenant jusqu'à 6 octets), au lieu des 7 types d’octets finalement retenus pour ne coder (dans des séquences comprenant aussi jusqu'à 6 octets) que les points de code jusqu'à 31 bits dans la version initiale d'UTF-8 (publiée par le Consortium X/Open sous le nom FSS-UTF, puis proposé par le comité technique d’ISO 10646 comme la proposition « UTF-2 » alors encore en concurrence avec la proposition « UTF-1 », jusqu'à ce que la proposition UTF-2 soit retenue et adopte le nom UTF-8 déjà retenu et utilisé dans X/Open et Plan 9).

Ce codage UTF-8 a été restreint encore lorsque Unicode et ISO 10636 ont convenu de n'allouer des caractères que dans les 17 premiers plans afin de maintenir indéfiniment la compatibilité avec UTF-16 (sans devoir le modifier), en restreignant les séquences jusqu'à 4 octets seulement et en n’utilisant que les 5 premiers des 7 types d'octets (ce qui a nécessité de définir comme invalides de nouvelles valeurs d’octet et certaines séquences d'octets pourtant valides individuellement).

Prise en charge

  • Navigateurs web : la prise en charge d'UTF-8 commença à être répandue à partir de 1998.
  • Les anciens navigateurs web ne supportant pas UTF-8 affichent tout de même correctement les 127 premiers caractères ASCII.
  • Netscape Navigator supporte UTF-8 à partir de sa version 4 (juin 1997).
  • Microsoft Internet Explorer supporte UTF-8 à partir de sa version 4 (octobre 1997) pour Microsoft Windows et pour Mac OS (janvier 1998).
  • Les navigateurs basés sur le moteur de rendu Gecko (lancé en 1998) supportent l'UTF-8 : Mozilla, Mozilla Firefox, SeaMonkey etc.
  • Opera supporte UTF-8 à partir de sa version 6 (novembre 2001).
  • Konqueror supporte UTF-8.
  • Safari sur Macintosh et Windows supporte UTF-8.
  • OmniWeb sur Macintosh supporte UTF-8.
  • Chrome de Google supporte UTF-8.
  • Fichiers et noms de fichiers : de plus en plus courant sous les systèmes GNU/Linux, pas très bien supporté sous Windows.
  • Client de messagerie
  • Thunderbird supporte UTF-8
  • Si.Mail supporte UTF-8
  • Lotus Notes supporte UTF-8

Extensions non standards

Toutefois, des variantes d’UTF-8 (basées sur les possibilités de codage de la version initiale non restreinte) ont continué à être utilisées (notamment dans l’implémentation de la sérialisation des chaînes Java) pour permettre de coder sous forme d'un échappement multioctets certains caractères ASCII réservés normalement codés sur un seul octet (par exemple le caractère nul).

De plus, certains systèmes utilisent des chaînes de caractères non restreints : par exemple, Java (et d’autres langages y compris des bibilothèques de manipulation de chaînes en C, PHP, Perl, etc...) représentent les caractères avec des unités de codage sur 16 bits (ce qui permet de stocker les chaînes en utilisant le codage UTF-16, mais sans les contraintes de validité imposées par UTF-16 concernant les valeurs interdites et l'appariement dans l’ordre des « demi-points de code » ou surrogates) ; dans ce cas, les unités de codage sont traitées comme des valeurs binaires et il est nécessaire de les sérialiser de façon individuelle (indépendamment de leur interprétation possible comme caractères ou comme demi-points de code). Dans ce cas, chaque unité de codage 16 bits qui représente un « caractère » (non-contraint) est sérialisé sous forme de séquences comprenant jusqu'à 3 octets chacune, et certains octets interdits par l’implémentation (par exemple les caractères nuls ou la barre de fraction « / » dans un système de fichiers ou d’autres caractères codés sur un octet dans d’autres protocoles) sont codés sous forme de séquences d’échappement à deux octets dont aucun n’est nul, en utilisant simplement le principe de codage de la première spécification de FSS-UTF (avant celle qui a été retenue par le Consortium X/Open dans sa RFC initiale où ces échappements étaient spécifiquement interdits et le sont restés).

Avant l’adoption de la proposition UTF-2 retenue pour UTF-8, il a également existé une variante UTF-1, où les codages multiples étaient impossibles, mais nécessitait un codage/décodage plus difficile devant prendre en compte la position de chaque octet et utilisant un certain nombre de valeurs « magiques ».

Ces variantes ne doivent pas être appelées « UTF-8 ».

Une de ces variantes non standards a fait cependant l’objet d’une standardisation ultérieure (en tant qu’alternative à UTF-16 et utilisant des paires de demi-codets codés chacun sur 3 octets) : voir CESU-8.

Exemple de variante utilisée en Java

Par exemple les API d’intégration des machines virtuelles Java (pour JNI, Java Native Interface ou pour la sérialisation des classes précompilées), qui permettent d’échanger les chaînes Java non contraintes sous forme de séquences d’octets (afin de les manipuler, utiliser ou produire par du code natif, ou pour le stockage sous forme de fichier natif codés en suites d’octets) sont suffixées par "UTFChars" ou "UTF", mais ce codage propre à Java n’est pas UTF-8 (La documentation de Sun la désigne comme modified UTF, mais certains documents plus anciens relatifs à JNI désignent encore ce codage incorrectement sous le nom UTF-8, ce qui a produit des anomalies de comportement de certaines bibliothèques natives JNI, notamment avec les API systèmes d’anciennes plateformes natives qui ne supportent pas nativement les codages de caractères sur plus de 8 bits), car :

  • le caractère nul, présent en tant que tel dans une chaîne Java, est codé sous forme de deux octets non nuls (et non un seul octet nul utilisé pour indiquer la fin de séquence).
  • les surrogates (U+D000 à U+D7FFF) peuvent être codés librement, dans un ordre quelconque, de même que les points de code interdits normalement pour le codage de caractère (par exemple U+FFFF ou U+FFFE) : aucun test de validité n'est demandé
  • les séquences d’octets plus longues (sur 4 octets pour représenter les caractères hors du plan multilangue de base) normalisées et valides dans UTF-8 ne sont pas reconnues par la machine virtuelle dans ses API basées sur modified UTF (ce qui déclenche des exceptions lors de la conversion demandée par le code natif de la chaîne 8 bits vers une chaîne Java gérée par la machine virtuelle) : il faut alors réencoder les caractères hors du plan de base (codés sur 4 octets en UTF-8) sous la forme de deux séquences de 3 octets en modified UTF, une pour chaque surrogate.
  • les chaînes Java (de la classe système String) et le type numéral char sont utilisés aussi pour le stockage (sous forme compacte, non modifiable et partageable) de données binaires quelconques (pas seulement du texte), et peuvent aussi être manipulées dans d’autres codages que l’UTF-16 (la seule contrainte étant que les unités de codage individuelle ne doivent pas dépasser 16 bits et doivent être de valeur positive, le bit de poids fort n'étant pas évalué comme un bit de signe).

En conséquence,

  • les applications écrites en pur Java (sans code natif) et qui nécessitent l’implémentation de contraintes de codage pour être conformes à Unicode pour le texte doivent le demander explicitement et utiliser un des filtres de codage fournis (pour UTF-8, comme aussi pour UTF-16), ou construire et utiliser des classes basées sur la classe String et le type numéral char.
  • un texte UTF-8 valide (et manipulé en code natif dans des chaînes sans caractères nuls) nécessite un prétraitement avant de pouvoir être transmis à la machine virtuelle Java via JNI ; notamment, toute séquence codée sur 4 octets (pour un caractère hors du plan de base) doit être transcodée en deux séquences de 3 octets.
  • les chaînes obtenues depuis une machine virtuelle Java via les interfaces UTF de JNI nécessitent un prétraitement de contrôle de validité ou de filtrage dans le code natif, avant de pouvoir être utilisées comme du texte UTF-8 valide (il faut détecter les occurrences du caractère null codé en deux octets et, si ce caractère est acceptable par le code natif, le transcoder en un seul octet ; il faut vérifier dans le code natif l’appariement correct des surrogates, codés chacun sur 3 octets, et les filtrer si ces séquences ne sont pas refusées comme invalides, puis transcoder toute paire valide de surrogates en une seule séquence de 4 octets seulement et non deux séquences de 3 octets).