On a vu au chapitre C3‑VIII  que les extensions du jeu ASCII restreint via les normes ISO/IEC‑8859 ou des pages de code Windows est malcommode.

En effet, pour prendre en charge l'ensemble des caractères utilisés par les langues asiatiques ou même certains textes multilingues (par exemple, avec la combinaison français‑russe), ou encore un grand nombre de symboles graphiques (signes mathématiques, émoticônes…), on serait contraint de coder fréquemment des instructions de changement de jeu étendu ou de la page de code active.

En 1991, à l'initiative d'une association californienne à but non lucratif appelée Consortium Unicode W, le standard Unicode a été crée pour constituer le jeu universel des caractères dit UCS (universal coded character set). Soutenu par les grandes entreprises du secteur (Apple, Xerox, Microsoft, IBM…), ce standard a fait l'objet d'une norme internationale ISO/IEC 10646 W qui a aussi introduit plusieurs formats d'encodage nommés UTF (Unicode transformation format), chacun étant bien adapté à tel ou tel contexte informatique (système d'exploitation, application de messagerie, etc.). En particulier, le format UTF‑8 s'est depuis largement imposé, notamment pour l'encodage des caractères des pages web et pour les entrées‑sorties dans les systèmes d'exploitation Linux ainsi que dans de nombreux logiciels – éditeurs de code, traitement de texte, etc.

Bien sûr, des langages de programmation généralistes comme C et C++ ont évolué pour permettre au codeur de manipuler toutes les valeurs de caractères Unicode. Mais en contre‑partie, les techniques de programmation sont plus complexes que pour les caractères ASCII, et elles dépendent du format d'encodage adopté :

  • si le format est à taille fixe comme UTF‑32, les valeurs de caractères peuvent être manipulées comme des données de types caractères larges ;
  • mais si le format est à taille variable comme UTF‑8 ou UTF‑16, les valeurs de caractères ne peuvent être manipulées qu'au sein de chaînes de caractères (simples ou larges), via des algorithmes d'encodage multibyte, qui seront abordés seulement au chapitre C5‑VI .

Ce cours, qui ne constitue qu'une introduction aux caractères larges, se donne les objectifs suivants :

  • introduire préalablement le standard Unicode – son catalogue et son espace de codage ;
  • présenter les trois formats d'encodage UTF‑32, UTF‑16 et UTF‑8, en détaillant leurs principes, avantages et inconvénients ;
  • et enfin d'aborder les types de caractères larges, en exposant essentiellement :
    • la syntaxe de codage des valeurs de caractères ;
    • les opérations que l'on peut effectuer sur ces valeurs, sachant qu'elles sont très similaires à celles sur les valeur de caractères simples (cf. chap. C3‑VIII ).

Le standard Unicode

Le standard Unicode W comporte 3 aspects :

  • le jeu universel des caractères (UCS),
  • l'espace de codage – ou de numérotation – des caractères,
  • les différents formats d'encodage en mémoire des caractères – principalement UTF‑32, UTF‑16 et UTF‑8.

Les formats d'encodage étant très techniques, ils seront présentés dans la section suivante qui leur est dédiée.

Le jeu universel des caractère

Le jeu universel des caractères – ou UCS pour universal coded character set W, abrégé « abusivement » par le nom du standard lui‑même Unicode – est un catalogue informatique qui répertorie déjà près de 160 mille caractères (Unicode version 17.0.0 – sept. 2025 ). Il est actualisé tous les ans par le comité technique du consortium Unicode.

L'universalité n'est pas un vain mot. L'UCS a pour vocation de cataloguer :

  • tous les caractères « lettres » au sens large (c'est‑à‑dire, aussi les idéogrammes) permettant d'écrire toutes les langues du monde, qu'elles soient contemporaines ou anciennes ;
  • tous les symboles mathématiques, graphiques, musicaux, émoticônes, etc. qui sont reconnus par la norme Unicode (il y a chaque année de nouvelles entrées) ;
  • tous les caractères de contrôle non pourvus de glyphe mais employés par les systèmes numériques (saut de tabulation, de ligne, etc.), même ceux qui ne sont plus usités.

Pour consulter l'UCS et y rechercher un caractère particulier, que ce soit pour une opération de « copier/coller » ou pour obtenir ses caractéristiques (point de code, codes UTF, etc.), il existe plusieurs moyens :

  • On dispose des pages au format pdf, classifiées par familles de langues ou d'alphabets, au lien officiel suivant .
  • On trouve également des pages web dynamiques comme par exemple celle‑ci qui constitue un véritable catalogue en ligne,  .
  • On peut aussi recourir à la table des caractères du système d'exploitation d'un poste de travail. Cet utilitaire permet d'avoir une bonne vue d'ensemble des différents blocs de classification et possède des fonctions de recherche (cf. la capture d'écran de celle de Linux Mint ci‑dessous).

Chaque année, le comité technique du consortium Unicode et ses groupes de travail se réunissent plusieurs fois par ans pour examiner et débattre des évolutions du standard, et tout particulièrement des propositions d'ajout de nouveaux caractères au catalogue. N'importe quelle personne ou organisation peut soumettre des propositions en suivant la procédure spécifiée sur le site du consortium  . Les décisions sont ensuite publiées (releases) une fois par an et entérinées par l'évolution de la norme ISO/IEC 10646.

On peut avoir un aperçu global de ces évolutions au lien suivant W. Ainsi, à titre d'exemple, depuis 2021 (version 14.0.0), plus de 15 mille caractères ont été ajoutés.

Définition d'un caractère – notion de point de code

Dans l'UCS, chaque caractère (cf. l'exemple en figure ci‑dessous ) est défini par :

  • son nom officiel dans les principales langues du monde ;
  • son point de code W, c'est‑à‑dire son numéro d'ordre unique dans le jeu, noté sous forme hexadécimale et préfixé par U+ ;
  • son glyphe W ou, s'il s'agit d'un caractère de contrôle, l'action qu'il accomplit.

Le glyphe d'un caractère peut différer selon la police employée pour l'affichage ou l'impression (cf. ci‑contre différents styles de glyphe du caractères Winking face). Il peut être source de confusion entre des caractères différents mais qui se ressemblent (cf. les exemples infra).

En revanche, le nom et surtout le point de code d'un caractère sont l'un et l'autre uniques ; ils permettent d'identifier le caractère sans ambiguïté.

Pour souligner l'importance de l'unicité du point de code d'un caractère, observons qu'en typographie, il faut ne pas confondre :

  • « - » appelé trait d'union - signe moins (en anglais, hyphen‑minus) dont le point de code est U+002D ; outre sa fonction de trait d'union dans les textes, c'est aussi le symbole « moins » qui est employé en programmation comme opérateur de soustraction ;
  • «  » appelé trait d'union insécable (en anglais, non‑breaking hyphen), dont le point de code est U+2011 ; contrairement au trait d'union usuel (dont il a pratiquement la même apparence dans les polices de caractères proportionnelles), il interdit aux logiciels de traitement de textes et aux navigateurs de pages web de séparer par un saut de ligne les deux mots qu'il relie ;
  • «  » appelé signe moins (en anglais, minus sign), de point de code U+2212 ; c'est le symbole « moins » qui est employé en édition mathématique ; il est plus fin et plus long que le trait d'union, mais un œil inexpérimenté pourrait ne pas percevoir cette différence.
  • «  » appelé tiret demi‑cadratin  ou tiret moyen W (en anglais, En dash), de point de code U+2013 ; c'est le symbole de ponctuation qui est très employé en typographie française, notamment les répliques dans les dialogues ou pour délimiter les proposition incises W dans un texte.

On voit bien à travers ces exemples le risque élevé de confusion pour un débutant qui se contenterait d'identifier les caractères par leur glyphe ou par une lecture approximative de leur nom.

L'espace de codage

Le standard Unicode définit un espace de codage qui va des points de code U+000000 à U+10FFFF.

Cet espace compte donc plus d'un million de points de code, ce qui semble a priori très largement suffisant pour tous les besoins actuels et à venir, puisqu'à l'heure actuelle, il y a moins de 150 000 caractères dans l'UCS – cf. supra .

Les points de codes ne sont pas des codes informatiques à proprement parler, ce sont seulement des numéros. Néanmoins, ils constituent une base pour générer les différents formats d'encodage UTF.

Division en plans

L'espace de codage Unicode est divisé en 17 plans, chacun comptant 65 536 points de code W.

  • Le plan 0, appelé plan multilingue de base – ou BMP pour basic multilingual plane W – va des points codes U+000000 à U+00FFFF. Tout caractère de ce plan peut donc être encodé sur seulement deux octets.
  • Sur la figure ci‑dessous (source Wikipedia W), chaque carré numéroté (en hexadécimal) représente un sous‑ensemble de 256 points de code, dont le premier, colorié en noir (n° 00) contient les blocs dit Latin de base et Supplément Latin‑1. Ils correspondent au jeu ASCII restreint et l'extension ISO/CEI 8859‑1 (cf. chap. C3‑VIII ).
    Les 256 carrés du BMP forment donc bien 256 × 256 = 65 536 points de code. Attention, un carré n'est pas un bloc (cf. infra ).
    • Le BMP regroupe la totalité de tous les caractères de presque toutes les langues contemporaines. Exception notable, les trois principales langues asiatiques – chinois, japonais et coréen, formant le groupe CJK W – ne sont que partiellement incluses (tout en y occupant la majorité du plan – cf. les carrés de couleur saumon  sur la figure ci‑contre).
    • Le BMP comporte également de très nombreux symboles (numéros, flèches, symboles mathématiques, pictogrammes, etc. – cf. les carrés de couleur magenta ).
  • Le plan 1, dit plan multilingue supplémentaire – ou SMP pour supplementary multilingual plane W – va des points codes U+010000 à U+01FFFF. Il regroupe les caractères des langues anciennes, ainsi que des symboles d'usage contemporain (dominos, cartes à jouer, émoticônes, etc.)
  • Les plans 2 & 3 sont respectivement dits :
    • plan sinographique complémentaire – ou SIP pour supplementary ideographic plane – il va des points codes U+020000 à U+02FFFF ;
    • plan sinographique ternaire – ou TIP pour tertiary ideographic plane – il va des points codes U+030000 à U+03FFFF ;
    Ces deux plans sont consacrés aux autres caractères des langues du groupe CJK qui, faute de place, ne rentrent pas dans le BMP.
  • Les plans 4 à 16 sont soit non affectés, soit spéciaux, soit privés.

Division d'un plan en blocs Unicode

À l'exception des plans d'usage privé (nº 15 & 16), tout plan de l'UCS se divise lui‑même en blocs dits Unicode pour faciliter le repérage des caractères.

Un tel bloc Unicode W est un ensemble de points de codes successifs identifié par un nom et formant un tout cohérent au regard d'un usage spécifique – typiquement, un jeu de caractères alphabétiques particulier (grec, cyrillique, etc.).

Au début du BMP, on trouve les blocs Unicode suivants :

  • le bloc Commandes CO et Latin de base  (codes U+0000 à U+007F), qui est équivalent au jeu ASCII restreint (cf. chap. C3‑VIII ) ;
  • le bloc Commandes C1 et supplément Latin‑1  (codes U+0080 à U+00FF), qui est équivalent à la partie étendue du jeu ISO‑8859‑1 (cf. chap. C3‑VIII ) ;
  • les blocs Latin étendu A  (codes U+0100 à U+017F) et Latin étendu B  (codes U+0180 à U+024F) qui viennent compléter le bloc précédent pour couvrir la totalité de l'alphabet latin  – on y trouve notamment les ligatures « Œ » et « œ »  du jeu ASCII étendu dit Latin‑9 (cf. chap. C3‑VIII ) ;
  • le bloc Supplément pour l'alphabet phonétique international  (codes U+0250 à U+02AF) ;
  • etc.

La taille d'un bloc Unicode n'est pas uniforme, elle varie de quelques dizaines de points de code à plusieurs dizaines de milliers, en fonction des regroupements fonctionnels d'attribution des points de code.

La liste exhaustive des blocs Unicode peut être trouvée notamment ici  ou là .

Les formats d'encodage associés au standard Unicode

Les formats UTF – pour Unicode transformation format – sont des formats d'encodage qui transforment des points de code du jeu universel des caractères en de véritables codes machines pour le stockage en mémoire des valeurs de caractères.

Il existe à l'heure actuelle trois principaux formats – UTF‑8, UTF‑16 et UTF‑32 – dont le nombre indiqué en suffixe précise la largeur en bits de l'unité d'encodage en mémoire du format.

Le format UTF‑32

UTF‑32 W est un format d'encodage à taille fixe : chaque caractère est encodé sur une et une seule unité d'encodage constituée d'un mot binaire de 32 bits (4 octets).

Le principe d'encodage de ce format est le plus simple  qui soit : l'unité d'encodage d'un caractère est directement son point de code dans l'UCS, exprimé sur 8 digits hexadécimaux (8 × 4 = 32 bits) – cf. la figure ci‑contre.

L'émoticône 😉 nommé Winking face  dont le point de code est U+0001F609 est directement encodé 00 01 F6 09 au format UTF‑32.

La simplicité du format UTF‑32 se paye par coût mémoire très élevé. Ainsi, pour un texte en langue européenne encodable en ASCII étendu (un octet par caractère), l'espace mémoire nécessaire devient 4 fois supérieur s'il est encodé en UTF‑32. Il en résulte que ce format est finalement très peu employé.

Pour mesurer l'ampleur du coût mémoire du format UTF‑32, comparons l'encodage d'une chaîne de caractère de style C (avec un caractère NUL ajouté à la fin), comme par exemple "OK" :

  • au format ASCII :
  •    O        K        \0
    -------- -------- -------- 
    01001111 01001011 00000000
    
  • au format UTF‑32 :
  •                O                                  K                                \0
    -------------------------------- --------------------------------- ---------------------------------
    00000000000000000000000001001111 000000000000000000000000001001011 000000000000000000000000000000000
    

Plus généralement, rappelons que l'on peut coder environ 4 milliards de valeurs sur 32 bits (cf. chap. III‑2 ) alors que l'UCS lui‑même ne compte qu'environ 1 millions de points de code. Parmi ces points de code, seulement cent mille environ sont affectés à des caractères. Le format UTF‑32 est donc très largement surdimensionné.

En règle générale, le format UTF‑32 n'est donc envisagé que pour coder des fichiers de textes en langues asiatique ou anciennes comportant une grande majorité de caractères qui nécessitent chacun 4 octets d'encodage, quel que soit le format choisi parmi ceux existant.

  1. On pourrait penser pouvoir économiser un octet, puisqu'un format sur 24 bits (3 octets) serait en principe suffisant pour encoder le dernier point de code U+10FFFF. Néanmoins, ce format ne serait pas compatible avec les contraintes d'alignement (cf. chap. C3‑I ) des unités d'encodage sur la plupart des architectures de machines.
  2. Avec le format UTF‑32, le code de chaque caractères du plan BMP commence par deux voire trois octets nuls. Or dans un processus de lecture de bas niveau (octet par octet), les octets nuls se confondent avec le caractère NUL du jeu ASCII qui est souvent employé (notamment en langage C) pour marquer les fins de chaîne. C'est un autre inconvénient du format UTF‑32, car sans précautions particulières, une telle confusion peut faire échouer des algorithmes de lecture de bas niveau qui sont encore mis en œuvre dans de nombreuses applications (messagerie électronique, etc.).

Le format UTF‑16

UTF‑16 W est un format d'encodage à taille variable :

  • chaque caractère est encodé sur 1 ou 2 unité(s) d'encodage, selon le plan de l'UCS auquel le caractère appartient ;
  • chaque unité d'encodage étant formée d'un mot de 16 bits.

Le format UTF‑16 est notamment adopté pour l'encodage des caractères par :

  • les UEFI W – unified extensible firmware interface, c'est‑à‑dire les interfaces de firmware des cartes‑mères des postes de travail ;
  • les système d'exploitation Microsoft Windows ;
  • divers standards de langages de programmation (Java, JavaScript, etc.) et certains environnements de programmation (Qt…).

En revanche, UTF‑16 n'est quasiment pas employé pour les pages web car statistiquement, il est moins efficace que le format UTF‑8 en termes de coût mémoire global et de vitesse d'affichage des pages.

Sans entrer dans les détails, le principe général d'encodage des caractères en UTF‑16 est le suivant :

  • Tout caractère du BMP (cf. supra ) est identifié par son point de code exprimé sur 4 digits hexadécimaux, sachant qu'un digit hexadécimal correspond à 4 bits. Ce point de code peut donc être donc encodé directement sur 4 × 4 = 16 bits, soit un seul mot de 16 bits (2 octets).
  • La lettre « Œ » (ligature capitale O E ) dont le point de code est U+0152 est directement encodé 01 52 au format UTF‑16.

  • Tout caractère des autres plans est encodé de façon plus complexe sur deux mots de 16 bits en utilisant les 2048 points de code réservés du BMP U+D800 à U+DFF appelées paires de substitution – en anglais surrogate pairs W.
  • Pour comprendre le principe d'encodage en UTF‑16 d'un point de code hors du BMP, il n'est pas possible d'en rester à une décomposition en digits hexadécimaux ; il faut mener l'analyse au niveau binaire.
    En premier lieu, on peut faire les observations suivantes :
    • Les points de code hors du BMP s'échelonnent de U+10000 à U+1F0000. Ils s'expriment sur 6 digits hexadécimaux, mais le digit de poids fort est très peu exploité (il ne prend que les valeurs 0 ou 1, jamais 2, 3, etc.).
    • Donc, en soustrayant à tous les points de code la valeur 0x10000, on les ramène dans l'intervalle allant de 0x00000 à 0xFFFFF, avec à la clef un digit en moins. Il ne reste donc plus que 5 digits hexadécimaux à encoder, soit 5 × 4 = 20 bits, et sachant qu'on dispose de deux mots de 16 bits, soit 32 bits. Cela fait 12 bits supplémentaires que l'on peut exploiter.
    Avec ces 12 bits supplémentaires, on va donc produire localement (c'est‑à‑dire au sein même du code UTF‑16 du caractère) un encodage auto‑indexé. On affecte à chacune des deux unités d'encodage – qui sont des mots de 16 bits – un motif initial distinctif (on peut parler de préfixes binaires) de 6 bits :
    • le préfixe 110110 pour le mot de haut rang ;
    • le préfixe 110111 pour le mot de bas rang.
    Ensuite, on procède à la recopie des 20 bits du point de code ramené sur 5 digits, dans l'ordre de leur rang, sur les 10 bits vacants de chacun des deux mots du code UTF‑16, conformément à la figure ci‑dessous.
    Chacun de ces deux préfixes binaires permet de déterminer immédiatement – dès sa lecture – si le mot de 16 bits auquel il appartient est de haut rang ou de bas rang dans un code UTF‑16, sans avoir besoin de lire un mot voisin : c'est le principe de l'auto‑indexage local. Mais alors, il est indispensable qu'un tel mot ne corresponde pas au code UTF‑16 sur 2 octets d'un caractère du BMP, sinon la confusion serait fatale.
    Et c'est pourquoi, dans le BMP, tous les points de code :
    • allant de U+D800 (1101 1000 0000 0000) à U+DBFF (1101 1011 1111 1111),
    • et de U+DC00 (1101 1100 0000 0000) à U+DFFF (1101 1111 1111 1111),
    sont réservés et donc non affectés à des caractères – on parle de « non‑caractères ». On appelle ces points de code respectivement des mots de substitution haut et bas – en anglais, high and low surrogates. On parle également de paires de substitution car dans un fichier encodé en UTF‑16, ces mots ne peuvent aller l'un sans l'autre : ils vont donc nécessairement par paires.

    Considérons encore le cas de l'émoticône 😉 nommé Winking face  qui appartient au plan SMP (supplementary multilingual plane – cf. supra ). Son encodage au format UTF‑16 est D8 3D DE 09 conformément au schéma ci‑dessous.

    Il est formé de deux mots de 16 bits qui sont les paires de substitutions 0xD83D et 0xDE09.

  1. Un débutant pourrait être surpris par le fait que réserver 2048 points de code dans le BMP – les paires de substitution – soit suffisant pour l'encodage des dizaines de milliers de caractères hors du BMP. Mais il ne faut pas perdre de vue que ces points de code ne sont pas utilisés directement comme des codes. C'est bien entendu par la combinatoire des paires – soit 1024 × 1024 = 1048576 codes – que l'encodage au format UTF‑16 permet de couvrir tous besoins actuels et futurs.
  2. Comme pour le format UTF‑32 (cf. supra ), le format UTF‑16 peut poser des problèmes aux algorithmes de lecture de bas niveau (octet par octet) par confusion entre le marqueur de fin de chaîne – le caractère NUL – et les octets nuls présents dans les codes des caractères, notamment ceux au début du BMP.

Le format UTF‑8

UTF‑8 W est un format d'encodage à taille variable :

  • chaque caractère est encodé sur 1 à 4 unité(s) d'encodage, selon la largeur de son point de code en termes de nombre de bits à encoder ;
  • chaque unité d'encodage étant formée d'un mot de 8 bits, c'est‑à‑dire un octet.

Le format UTF‑8 est très largement adopté – et de plus en plus – dans de nombreux contextes de l'informatique, parce qu'il optimise le coût mémoire de stockage des fichiers contenant du texte, quelle que soit sa nature (documents, code, etc.) :

  • les pages web – et donc, les logiciels navigateurs ;
  • les fichiers bureautiques au format OpenDocument W ;
  • les systèmes d'exploitation comme Unix, Linux, Android 
  • les nombreux langages de programmation et IDE – notamment, C/C++, Python, Arduino

L'encodage au format UTF‑8 recourt à l'auto‑indexation systématique des unités d'encodage par des motifs initiaux (ou préfixes) sur leurs bits de hauts rangs. Ces motifs sont différents selon le nombre d'unités d'encodage nécessaires à l'encodage :

  • Si le caractère est encodé sur un seul octet, ce dernier prend le motif initial 0 sur son bit de haut rang (d7).
  • Sinon :
    • Le motif initial de l'octet de tête – c'est‑à‑dire de haut rang (leading byte) – un motif initial formé de n bits à 1, autant que le nombre d'octets du code du caractère, suivi d'un 0.
    • Donc : 110 pour un caractère codé sur 2 octets, 1110 pour un caractère codé sur 3 octets, etc.
    • Le motif initial des octets de suite (trailing bytes) est 10.

Tous les autres bits de l'octet de tête et des octets de suite forment l'espace d'encodage disponible. Le tableau ci‑dessous récapitule ces différents éléments.

Plans
UCS
Points
de code
Nbre
octets
Format des octets Espace d'encodage
(bits •)
0
(BMP)
U+0000
à U+007F
1 0••• ••••  (idem que l'ASCII restreint) 7 bits
128 valeurs
U+0080
à U+07FF
2 110• ••••  octet de tête
10•• ••••  octet de suite
5 + 6 = 11 bits
2 048 valeurs
U+0800
à U+FFFF
3 1110 ••••  octet de tête
10•• ••••  10•• ••••  octets de suite
4 + 6×2 = 16 bits
65 536 valeurs
⩾ 1 U+010000
à U+10FFFF
4 1111 0•••  octet de tête
10•• ••••  10•• ••••  10•• ••••
3 + 6×3 = 21 bits
plus de 2 × 106 valeurs

Ensuite, le principe d'encodage des bits est le suivant :

  • retranscrit en binaire naturel, les valeurs des bits du point de code du caractère sont affectées aux bits de l'espace d'encodage à partir du rang 0 et dans l'ordre croissant des rangs ;
  • les bits de hauts rangs non employés sont mis à 0.

  1. Le symbole « & » (ampersand ) de point de code U+0026 (donc compris entre U+0000 et U+007F) est directement encodé en UTF‑8 sur un seul octet 26.
  2. La lettre « Œ » (latin capital ligature O E ) de point de code U+0152 (donc compris entre U+0080 et U+07FF) est encodé en UTF‑8 sur les 2 octets C5 92, conformément au schéma ci‑dessous.
  3. Le symbole « € » (euro sign ) de point de code U+20AC (donc compris entre U+0800 et U+FFFF) est encodé en UTF‑8 sur les 3 octets E2 82 AC, conformément au schéma ci‑dessous.
  4. L'émoticône 😉 (winking face ) de point de code U+1F609 (donc compris entre U+010000 et U+10FFFF) est encodé en UTF‑8 sur les 4 octets F0 9F 98 89, conformément au schéma ci‑dessous.

  1. Un intérêt essentiel du format UTF‑8 est son identité d'encodage avec le format ASCII restreint, qui offre une parfaite rétrocompatibilité de tous les fichiers codés dans ce format ancien.
  2. De plus, grâce au choix judicieux des motifs initiaux, aucun caractère ne peut inclure dans son code un octet nul à part bien sûr le caractère NUL lui‑même qui a le code 0x0. Ce dernier peut donc garder sa fonction usuelle de caractère de fin de chaîne qui déclenche la terminaison des processus de lecture de bas niveau.
  3. Le choix des motifs initiaux est judicieux à plus d'un titre. En particulier, il facilite le tri par ordre lexicographique (collation – cf. chap. C5‑VI ) : si l'octet de tête d'un caractère est supérieur à celui d'un autre, alors cette relation d'ordre vaut aussi entre leurs points de code respectifs.
  4. Même en réquisitionnant des bits pour l'auto‑indexation des octets, l'espace d'encodage de l'UTF‑8 reste beaucoup plus grand que l'ensemble des points de code du standard Unicode. Cela garantie la possibilité de prendre en charge de nouveaux caractères dans le futur.
  5. Sur seulement deux octets, le format UTF‑8 permet de coder beaucoup moins de caractères (environ 2 000) que le format UTF‑16 (environ 65 000). Mais globalement, sauf pour les fichiers en langues asiatiques, le format UTF‑8 est quand même plus efficace. En effet, dans la majorité des fichiers source (documents, code, etc.), les caractères dont le point de code est supérieur à U+07FF – donc nécessitant 3 octets d'encodage – sont statistiquement rares. Leur prise en charge ne compromet pas l'économie d'un octet obtenue sur les caractères très fréquents – ceux du jeu ASCII restreint.

Conventions de boutisme

Les conventions de boutismes ont été évoquées dans le contexte général des systèmes informatiques au chapitre C3‑I . Elles existent également pour l'encodage des caractères larges (puisqu'ils sont encodés sur plusieurs octets chacun) – et tout particulièrement dans les formats UTF‑16 et UTF‑32.

Il est possible de signaler la convention de boutisme d'un flux de caractères – chaîne ou fichier – par l'ajout préliminaire au début du flux d'un caractère spécial appelé byte order mark W ou BOM).

En UTF‑16 comme en UTF‑32, le BOM est signalé par le point de code U+FEFF , quelle que soit la convention adoptée.

Inclus dans le BMP, le point de code U+FEFF est défini comme un espace insécable sans chasse, autrement dit un glyphe vide de largeur nulle – et donc visuellement indétectable – derrière lequel aucun saut de ligne ne peut être automatiquement inséré lors d'une mise en page.

C'est l'application effective de la convention de boutisme sur le BOM qui permet aux logiciels traitant le flux de caractères de détecter la convention – et c'est pourquoi on le qualifie de nombre magique W.

  • Avec le principe d'encodage du format UTF‑16 exposé supra , le BOM apparaît comme la séquence ordonnée d'octets :
    • FF FE en convention petit‑boutisme – et le format est alors désigné UTF‑16LE (little endian) ;
    • FE FF en convention gros‑boutisme – et le format est alors désigné UTF‑16BE (big endian).
  • Avec le principe d'encodage du format UTF‑32 exposé supra , le BOM apparaît comme la séquence ordonnée d'octets :
    • FF FE 00 00 en convention petit‑boutisme – et le format est alors désigné UTF‑32LE ;
    • 00 00 FE FF en convention gros‑boutisme – et le format est alors désigné UTF‑32BE.

Selon le standard Unicode, en l'absence de BOM dans un flux de caractères, c'est la convention gros‑boutisme est censée s'appliquer par défaut. Toutefois, certains système d'exploitation ne respectent pas cette règle. Ainsi, c'est le format UTF‑16LE qui est utilisé par défaut sous Windows.

Soulignons qu'avec le format UTF‑16 tout particulièrement, une erreur d'évaluation de la convention de boutisme n'est pas détectable automatiquement par une simple analyse binaire. En effet, le format UTF‑16 n'est pas globalement auto‑indexé : les codes de caractères avec leurs octets présentés en ordre inverse sont ceux d'autres caractères.

Considérons tout simplement la lettre « A ». Son code au format UTF‑16LE est le mot de 16 bits 0041 (exprimé en hexadécimal).

Toutefois, si on procède inversion de l'ordre des deux octets, ce mot devient 4100 et correspond au code de l'idéogramme «  » qui signifie « catastrophe » !

À la lumière de cet exemple trivial, on voit tout l'intérêt que présente le BOM pour éviter les confusions dans un flux de caractères encodé au format UTF‑16.

Cas du format UTF‑8

L'unité d'encodage en UTF‑8 étant d'un seul octet, la notion de convention de boutisme n'a en principe pas de sens dans ce format.

De plus, le format UTF‑8 est intégralement auto‑indexé. Grâce aux motifs binaires initiaux, il est facile de déterminer la convention de boutisme adoptée dès que cela devient nécessaire, c'est‑à‑dire lors de la lecture du premier caractère encodé sur au moins deux octets. En lisant son premier octet :

  • s'il s'agit d'un octet de tête, c'est la convention petit‑boutiste qui est adoptée ;
  • et s'il s'agit d'un octet de suite, c'est la convention gros‑boutiste.

Néanmoins, certains logiciels – par exemple, l'éditeur de code de l'IDE Microsoft VisualStudio – placent un caractère BOM au début de tout fichier encodé en UTF‑8, dans l'intention de signaler l'emploi de ce format qui n'est natif sur les systèmes d'exploitation Microsoft Windows.

En UTF‑8, le BOM possède le code EF BB BF.

Les types de caractères larges

Généralités

Pour gérer des données de type caractère prenant des valeurs dans tous le catalogue Unicode – donc potentiellement encodées sur plusieurs octets – les langages C et C++ mettent à disposition du codeur trois types de caractères larges (en anglais, wide characters) : wchar_t, char16_t et char32_t.

Ces types sont intégrés dans le noyau du langage C++ depuis la norme C++11.

En langage C, ils ont été introduits par des fichiers d'en‑tête de la bibliothèque standard :

  • wchar_t est déclaré dans les fichiers stddef.h et wchar.h ;
    • sa taille vaut 2 ou 4 octets, selon l'implémentation ;
    • c'est un type ancien, maintenu pour des questions de compatibilité, mais qu'il est préférable de ne plus utiliser, en privilégiant les types à taille spécifiée décrits ci‑dessous ;
  • char16_t et char32_t sont déclarés dans le fichier uchar.h ;
    • leur taille est spécifiée en nombre de bits par leur identificateur, quelle que soit l'implémentation (exactement comme les types int8_t, int16_t) ;
    • ils sont conçus pour constituer les unités d'encodage des caractères, respectivement dans les formats UTF‑16 et UTF‑32.

De plus, la norme C++20 a introduit le type char8_t pour compléter la série (cf. sa présence dans la liste des mots‑clef du langage C++). Il ne s'agit pas à proprement parler d'un type de caractère large, puisqu'il a les mêmes caractéristiques que unsigned char, mais on peut l'assimiler comme tel en raison de l'usage qui en est fait pour constituer les unités d'encodage du format UTF‑8.

Notons par ailleurs qu'en langage C, il reste possible d'employer simplement le type char pour mémoriser les octets des caractères encodés au format UTF‑8 dans la mesure où ce type possède bien 256 valeurs. Le fait que, le plus souvent la moitié de ces valeurs soient négatives (cf. chap. C3‑II ) n'est pas un problème rédhibitoire. Toutefois, il importe de ne jamais perdre de vue cet aspect dans les algorithmes programmés, en particulier lorsqu'on opère des tests de comparaison numérique sur les codes des caractères. Il suffit en principe d'opérer une conversion dans le type unsigned char lorsque cela est nécessaire.

Le programme académique ci‑dessous emploie une chaîne de caractères u8"Ça" encodée au format UTF‑8. Elle est déclarée comme un pointeur (cf. chap. C2‑VII ) dans le type signed char* pour montrer le fait qu'un type d'encodage signé ne pose pas de problème.

#include <stdio.h>

signed char * text = u8"Ça";

int main(void)
{
    printf("\"%s\" : ", text);
    while (*text) printf("0x%hhX ", *text++);
    return 0;
}  

L'instruction de la ligne nº7 affiche la chaîne. Quand à la ligne nº8, elle affiche successivement le code hexadécimal respectif de chaque octet d'encodage des caractères de cette chaîne (c'est‑à‑dire tels qu'ils sont mémorisés) via la spécification de conversion %hhX, donc dans le type unsigned char. Avec OnlineGDB, on obtient la sortie attendue :

"Ça" : 0xC3 0x87 0x61  

En effet, on peut vérifier qu'au format UTF‑8:

  • la majuscule « Ç » (point de code U+00C7 ) a bien le code C3 87 ;
  • la minuscule « a » (point de code U+0061 ) a bien le code 61.

On verra ainsi au chapitre C5‑VI  que les fonctions de la bibliothèque standard du langage C qui opèrent sur les chaînes de style C ont leurs arguments formels le plus souvent déclarés dans le type char*. Néanmoins, elles sont opérationnelles pour des chaînes de caractères encodées au format UTF‑8.

Codage des valeurs de types caractères larges

Dans un programme source en langages C et C++, on code des valeurs de caractères larges :

  • avec la même syntaxe que celle des caractères simples, pour mémoire (cf. chap. C3‑VIII ) :
    • avec des guillemets simples '',
    • et entre les guillemets, soit par syntaxe automorphe , soit par séquences d'échappement.
  • mais en employant un préfixe d'encodage :
    L, u ou U respectivement pour les types wchar_t, char16_t et char32_t.

Toutefois, il faut bien prendre conscience qu'avec les guillemets simples '' ne permettent pas l'expression de valeurs de caractères qui nécessitent plusieurs unités d'encodage dans un format donné.

En particulier, même si le compilateur traite le code source en UTF‑8 (ce qui est le cas avec GCC sous Linux et en Arduino), en langages C/C++ :

  • Sans préfixe, il n'est pas possible d'exprimer avec les guillemets simples '' d'autres caractères que ceux du jeu ASCII restreint.
  • Avec les préfixes L, u ou U, les expressions formées sont de types incompatibles avec les fonctions usuelles d'affichage printf ou Serial.print en Arduino.
  • Il est alors impératif d'utiliser des fonctions spécifiques d'affichage de caractères larges comme par exemple wprintf déclarée dans le fichier d'en‑tête wchar.h.

Le programme académique ci‑dessous permet d'afficher correctement la valeur de caractère large codée L'é' dans le terminal d'exécution.

#include <locale.h>
#include <wchar.h>

int main(void)
{
  setlocale(LC_ALL, ""); // Set the locale to the user's default
  wprintf(L"%lc\n", L'é');
  return 0;
}

Remarque : à la ligne nº 6, on modifie la variables d'environnement LC_ALL pour lui donner la valeur par défaut nécessaire dans le terminal d'exécution.

Avec OnlineGDB, on obtient bien é.

Et ce programme fonctionne en syntaxe automorphe avec n'importe quel caractère de l'UCS.

Syntaxe automorphe

Dans un éditeur de code, pour permettre la syntaxe automorphe de toutes les valeurs de caractères imprimables de l'UCS – c'est‑à‑dire directement par les glyphes des caractères, qui peuvent être générés par les touches du clavier ou par copier‑coller depuis une table des caractères – il est recommandé de choisir le format UTF‑8 pour le fichier source.

  • Avec Sublime Text (cf. chap. C1‑II ), ce choix est en général celui par défaut (cf. la valeur de l'attribut default_encoding dans le fichier de préférence du répertoire Default ouvert via le menu Preferences/Settings). sinon, il faut coder ce choix dans le fichier de préférence du répertoire User.
  • Rappelons (cf. chap. C3‑XIII  qu'à tout moment, on peut changer le format d'encodage du fichier via un menu contextuel auquel on accède en cliquant sur l'indication de format dans la barre de notifications en bas à droite de la fenêtre d'édition (cf. la capture d'écran ci‑contre). Il faut ensuite (re)enregistrer le fichier pour que la modification soit effective.
  • Avec VS Code (cf. chap. C1‑II ), on a le même principe avec un renvoi automatique à la palette de commande pour le choix du format d'encodage.

Syntaxe alternative par séquences d'échappement

Pour les caractères non imprimables, ou même si la syntaxe automorphe n'est pas opérationnelle sur l'éditeur de code, une syntaxe alternative permet de coder n'importe quelle valeur de caractère de l'UCS à partir du point de code U+A0 ; il suffit d'employer une séquence d'échappement de la forme :

  • \uxxxx où xxxx est le point de code à 4 digits hexadécimaux du caractère si ce dernier est dans le BMP (basic multilingual plane – cf. supra );
  • \Uxxxxxxxx où xxxxxxxx est le point de code à 8 digits hexadécimaux du caractère si ce dernier est dans un autre plan de l'UCS.

On rappelle (cf. chap. C3‑XIII ) que les valeurs de caractères des points de code U+00 à U+FF (inclus) sont quant à elles codables avec une séquence d'échappement préfixée par \x ou \X comme pour les caractères simples.

Tous les exemples ci‑après peuvent être testés en langage C dans l'environnement OnlineGDB en utilisant le programme générique suivant :

#include <stdio.h>
#include <uchar.h>

type c = préfixe'glyphe ou séquence d'échappement';

int main(void)
{
  printf("%x\n", c);
}

où :

  • la déclaration (ligne nº 4) est à adapter au cas par cas de chaque exemple ;
  • l'instruction d'affichage (ligne nº 8) affiche le code hexadécimal du caractère dans le format codé par le préfixe dans sa déclaration ; cela permet ainsi de vérifier que la déclaration du caractère a été interprétée correctement par le compilateur.
  • (L'affichage du glyphe du caractère est plus complexe à obtenir en tant que caractère individuel. Mais on verra au chapitre C5‑VI qu'un tel affichage est très simple au sein d'une chaîne de caractères codée au format UTF‑8.)
  1. La lettre « A » de l'alphabet latin (point de code U+41 ) :
    • se code U'A' ou U'\x41' dans le type char32_t ;
    • se code u'A' ou u'\x41' dans le type char16_t ;
    • se code 'A' ou '\x41' dans le type char (comme n'importe quel caractère du jeu ASCII restreint) ;
  2. La lettre « Ω » de l'alphabet grec (oméga – point de code U+3A9 ) :
    • se code U'Ω' ou U'\u03A9' dans le type char32_t ;
    • se code u'Ω' ou u'\u03A9' dans le type char16_t ;
    • mais ne peut pas se coder dans le type char puisque son point de code est supérieur à U+7F (il faudrait 2 unités d'encodage).
  3. l'émoticône 😉 (winking face – point de code U+1F609 ) :
    • se code U'😉' ou U'\U0001F609' dans le type char32_t (attention, elle ne peut être saisie par son glyphe que si la police de l'éditeur de code inclut ce caractère ou si le système d'exploitation procède à une substitution équivalente de police en cas d'absence) ;
    • mais ne peut pas se coder :
      • ni dans le type char puisque son point de code est supérieur à U+7F (il faudrait 4 unités d'encodage),
      • ni dans le type char16_t puisque son point de code est supérieur à U+FFFF (il faudrait 2 unités d'encodage).

Manipulation des données de types caractères larges

La manipulation des données de types caractères larges obéit aux mêmes principes généraux que la manipulation de données de type caractère simples (cf. chap. C3‑VIII ).

En effet :

  • puisque les types caractères – simples ou larges – sont fondamentalement des types entiers, on peut les manipuler comme tels, avec les opérateurs généraux ; toutefois, seules certaines opérations sont pertinentes en termes de valeurs de caractères (affectation, différence, comparaisons, etc.) ;
  • pour des opérations plus techniques, le fichier d'en‑tête wctype.h de la bibliothèque standard du langage C (cwctype pour le C++), fournit des fonctions de test et de conversion qui sont analogues à celles déclarées dans le fichier ctype, mais pour des données de type wchar_t C.

En fait, les fonctions de test et de conversion déclarées dans le fichier wctype.h opèrent sur des données de type wint_t (wide integer type ).

  • Dans la pratique, il s'agit d'une extension sur 32 bits du type wchar_t qui, rappelons‑le, est parfois limité à 16 bits. En particulier, le type wint_t permet d'inclure le caractère spécial WEOF (wide end‑‑file) qui est très employé dans le traitement des flux de données de types caractères larges.
  • De ce fait, les fonctions sont opérationnelles aussi pour les données de type char32_t (et aussi de type char16_t, bien entendu).

Les exemples ci‑après peuvent être testés dans l'environnement OnlineGDB en utilisant le programme générique suivant :

#include <stdio.h>
#include <uchar.h>
#include <wctype.h>
#include <locale.h>

int main(void)
{
  setlocale(LC_ALL, ""); // Set the locale to the user's default
  printf("%X\n", expression d'appel à tester);
  return 0;
}

et y faisant un copier‑coller des expressions d'appel à tester, proposées ci‑après.

  1. La fonction iswcntrl C (is wide control) teste si son argument est un caractère de contrôle.
    • L'expression iswcntrl(u'€') retourne la valeur 0 – nulle, donc équivalente à faux au sens booléen – ce qui est correct parce que le symbole monétaire «  » n'est pas un caractère de contrôle.
    • Et a contrario, l'expression iswcntrl(u'\t') est évaluée 2, donc vrai, ce qui est une fois de plus correct puisque la séquence d'échappement \t code le caractère de contrôle HT (saut de tabulation horizontale).
  2. La fonction iswupper C (is wide upper) teste si son argument est une lettre majuscule.
    • L'expression iswupper(u'Ç') retourne la valeur 1 – non nulle, donc équivalente à vrai au sens booléen, ce qui est correct puisque la lettre « Ç » est bien une majuscule.
    • Et a contrario, l'expression iswupper(u'ç') est évaluée 0, donc faux, ce qui est une fois de plus correct puisque la lettre « ç » n'est pas une majuscule.
  3. La fonction towlower C (to wide lower) retourne le code de la lettre minuscule correspondante à l'argument si ce dernier prend la valeur d'une lettre majuscule ayant une minuscule. Sinon, il retourne simplement le code l'argument.
    • L'expression towlower(u'Ç') retourne la valeur E7 qui est bien le code UTF‑16 de la lettre minuscule « ç » C.
    • L'expression towlower(u'€') retourne la valeur 20AC qui est le code UTF‑16 du symbole «  » C puisque ce dernier n'est pas une lettre, et donc ne peut être « converti » en minuscule.