UTF-16
UTF-16 est un schéma de codage de longueur variable de 16 bits et utilise le jeu de caractères UTF pour les points de code de caractères. Cela signifie qu’un caractère encodé en UTF-16 aura une unité de code de 16 bits.
Comme nous savons qu’un caractère codé en UTF-8 peut être représenté en 1 à 4 unités de code, un caractère UTF-16 peut être représenté en 1 ou 2 unités de code. Par conséquent, un caractère UTF-16 peut prendre 16 ou 32 bits de mémoire en fonction de son point de code.
Avant de sauter dans les spécifications de codage UTF-16, comprenons comment nous pouvons faire fonctionner UTF-16.
Comme nous avons une unité de code de 16 bits, en théorie, nous pouvons coder 21 caractères⁶ du point de code 0 à 65 535. Mais que se passe-t-il si nous avons un caractère dont le point de code est supérieur à 65 535? Dans ce cas, nous pouvons ajouter une autre unité de code.
Avec l’unité de code supplémentaire, nous pouvons coder un total de 232 caractères, ce qui fait plus de 4 M. Mais la question est alors de savoir comment un décodeur UTF-16 saura qu’il doit considérer 2 unités de code pour décoder un caractère?
UTF-8 a résolu ce problème en définissant les bits initiaux de la première unité de code et les unités de code de continuation sur des valeurs de bits spécifiques qu’un décodeur UTF-8 peut utiliser pour déduire le nombre d’unités de code qu’un caractère peut prendre.
Nous pouvons faire la même chose avec l’unité de code UTF-16 mais nous devons alors sacrifier quelques bits dans une unité de code pour cette fonctionnalité. Nous pouvons définir certains bits initiaux d’une unité de code à une valeur significative qu’un décodeur UTF-16 peut comprendre.
De plus, pour donner un pouvoir d’auto-synchronisation aux unités de code, une unité de code doit être capable de dire s’il s’agit de l’unité de code initiale ou d’une unité de code de continuation et non d’un caractère d’une seule unité de code.
Unicode a donc décidé de sacrifier les 6 bits initiaux de l’unité de code en ne laissant que 10 bits pour coder le point de code d’un caractère par unité de code. Si un caractère a besoin de 2 unités de code, 20 bits de la mémoire (sur 32 bits ou 4 octets) contiennent les informations de point de code réelles du caractère.
Alors, quels sont ces bits initiaux et comment ces bits font une entaille dans le jeu de caractères UTF? Suivons l’exemple ci-dessous.
1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
FIRST CODE UNIT---- SECOND CODE UNIT---
À partir de la norme UTF-16, la première unité de code doit commencer par 110110₂ et la deuxième unité de code doit commencer par 110111₂. Cela aidera un décodeur UTF-16 à comprendre lequel est la première unité de code et lequel est le second. Cela rend l’UTF-16 auto-synchronisé.
Maintenant, avec quoi nous avons 10 bits par unité de code à jouer, quelle est la plage dans laquelle nous pouvons jouer? En fin de compte, combien de caractères peuvent être codés dans deux unités de code de codage UTF-16?
Ne vous inquiétez pas, nous parlerons de caractères codés dans une seule unité de code.
Si vous jetez un coup d’œil aux modèles d’unités de code ci-dessus, nous avons une plage allant de 1101 1000 0000 0000₂ à 1101 1111 1111 1111₂. Cela équivaut à d800 to à DFFF₁₆.
The La première unité de code a une plage allant de d800₁₆ à 6fff and et la deuxième unité de code a une plage allant de dc00 DC à dfff₁₆. Nous pouvons obtenir ces valeurs en activant tous les bits de points de code: on et off.
Puisque UTF-16 doit être auto-synchronisé, les points de code entre d800₁₆ et dfff must ne doivent pas représenter un caractère en UTF-16. Étant donné que tous les encodages UTF suivent le même jeu de caractères UTF, ces points de code sont limités par UTF et ils ne sont et ne seront affectés à aucun caractère⁰.
Les points de code entre d800₁₆ et DFFF do ne représentent aucun caractère, ils sont donc appelés points de code de substitution ou ensemble, ils sont également appelés paires de substitution⁰.
Le premier point de code de substitution (à partir de la première unité de code) également appelé substitut élevé et le deuxième point de code (à partir de la deuxième unité de code) également appelé substitut faible. Soit un total de 2048 points de code, soit 1024 par mère porteuse.
💁♂ Les points de code de substitution ont sacrifié leur vie pour que nous puissions coder plus de caractères avec deux unités de code. Pensez à ça!
Donc la grande question, peut-on encoder un caractère avec une seule unité de code de UTF-16? La réponse est OUI. UTF-16 est un schéma de codage de longueur variable de 16 bits. Cela signifie-t-il que nous pouvons encoder 21 caractères⁶ avec une seule unité de code?
La réponse est NON. En théorie, on pourrait coder 21⁶ personnages avec des points de code 0000₁₆ (0₁₀) à FFFF₁₆ (65535₁₀), mais les points de code entre D800₁₆ et DFFF₁₆ ne représentent pas tous les caractères qu’ils sont réservés.
Il est donc sûr d’encoder des caractères de 0000₁₆ à d7ff and et e000 to à ffff leaving, ce qui représente 63 488 (65536-2048) caractères. Ceci est juste pour les caractères qui peuvent être codés dans une seule unité de code de UTF-16.
Comme nous avons un total de 20 bits à jouer en ce qui concerne les caractères que nous pouvons encoder en 2 unités de code de UTF-16, nous pouvons encoder 22 characters plus de caractères, soit 1 048 576 caractères.
Donc au total, nous pouvons coder 1 048 576 + 63 488 ce qui équivaut à 1 112 064 caractères (plus de 1 million de caractères). C’est la limite du jeu de caractères UTF. Puisque UTF-16 peut encoder ces nombreux caractères, les autres encodages UTF ne peuvent pas représenter des caractères au-delà de ceux-ci.
Points de code du jeu de caractères UTF
Comme nous savons qu’un point de code est une valeur décimale attribuée à un caractère, nous (Unicode) ne devons pas attribuer un point de code invalide à un caractère réel. Jusqu’à présent, les points de code invalides sont des points de code de substitution.
Avec une seule unité de code UTF-16, nous pouvons coder 63 488 caractères allant de 0000₁₆ à d7ff₁₆ et e000 to à ffff₁₆. Le dernier point de code est 65 535. Ceux-ci sont appelés caractères BMP (expliqués plus loin).
Avec deux unités de code de UTF-16, nous pouvons coder 1 048 576 caractères. Comme nous ne pouvons pas recommencer à partir de la valeur 0 (point de code) car ceux-ci viennent après les caractères BMP, nous devons les compenser de 65 536. Ces caractères sont appelés Caractères supplémentaires (expliqués plus loin).
Par conséquent, le premier caractère supplémentaire a une valeur de point de code de 65536 which, ce qui équivaut à 10000₁₆. Puisque nous pouvons coder 1 048 576 caractères avec deux unités de code de UTF-16, le dernier point de code est 1114111 which ce qui équivaut à 10ffff₁₆.
Alors décomposons les choses sous une forme tabulaire simple.
+-----------+---------------------+--------------------+
| UTF-16 CU | Code Point | |
+-----------+---------------------+--------------------+
| 1 | 0000₁₆ - D7FF₁₆ | valid |
+-----------+---------------------+--------------------+
| 1 | D800₁₆ - DFFF₁₆ | invalid(surrogate) |
+-----------+---------------------+--------------------+
| 1 | E000₁₆ - FFFF₁₆ | valid |
+-----------+---------------------+--------------------+
| 2 | 10000₁₆ - 10FFFF₁₆ | valid |
+-----------+---------------------+--------------------+
| | 110000₁₆ - FFFFFF₁₆ | unassigned |
+-----------+---------------------+--------------------+
Avec cette connaissance, voyons comment nous pouvons coder certains caractères en UTF-16. Choisissons un simple caractère ASCII A (point de code: 41₁₆), un caractère de la langue hindi (indienne) आ (prononcé Aa, point de code: 906 point) et une émoticône 😊 (appelée Happy face, point de code: 1f60a₁₆).
Comme on peut le voir dans le tableau ci-dessus, A et आ peuvent être codés dans une seule unité de code de UTF-16 car leurs valeurs sont inférieures à FFFF₁₆.
Lorsque nous devons coder un caractère en une seule unité de code, il nous suffit de convertir le point de code du caractère en un nombre binaire de 16 bits. Pour les caractères A, 00000000 01000001₂ est la représentation UTF-16.
De même, pour le caractère आ, il suffit de convertir son point de code 906₂ en un nombre binaire de 16 bits qui est 00001001 00000110₂.
Normalement, nous représentons des unités de code d’un caractère en nombres hexadécimaux. Par conséquent, pour le caractère A, la représentation UTF-16 est 0041₁₆ et de même, pour le caractère, la représentation UTF-16 is est 0906₁₆.
Pour le personnage 😊, les choses sont un peu différentes. Son point de code est 1f60a₁₆. Si nous regardons la table UTF-16 mentionnée ci-dessus, elle doit être codée en 2 unités de code d’UTF-16. Alors, comment commençons-nous?
Tout d’abord, nous devons soustraire 10000₁₆ du point de code. La raison en est que chaque caractère codé en 2 unités de code UTF-16 vient après les caractères BMP dont le dernier point de code est FFFF₁₆.
Par conséquent, pour obtenir la valeur réelle des bits utilisés pour le codage (qui est de 20 dans 2 unités de code), nous devons soustraire 10000₁₆ du point de code et utiliser le nombre final pour générer ces 20 bits.
💡 Pour vous aider à mieux comprendre, le premier caractère représenté avec 2 unités de code de UTF-16 aura tous ses 20 bits mis à 0. La valeur des bits utilisés pour coder le point de code de ce caractère est donc 0. Mais encore, son point de code est 10000 according selon le jeu de caractères Unicode. En effet, la valeur générée par ces 20 bits est ajoutée à 10000₁₆ pour générer le point de code final.
Comme vu précédemment, ces 2 unités de code ressemblent à celles ci-dessous.
1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
FIRST CODE UNIT---- SECOND CODE UNIT---
Il suffit de remplir ces 20 bits (x
) avec la valeur reçue du point de code de caractère. Le point de code du caractère 1 est 1f60a₁₆. Mais d’abord, nous devons en soustraire 10000 it. Nous obtenons f60a₁₆.
Maintenant, nous devons convertir f60a₁₆ en un nombre binaire de 20 bits et remplir les 20 bits dans le modèle d’unité de code ci-dessus. f60a₁₆ en binaire est 0000111101 1000001010₂. Maintenant, nous pouvons remplir ces 20 bits d’espace réservé.
Voici les unités de code finales.
1101 1000 0011 1101 1101 1110 0000 1010
0xD83D 0xDE0A
Le moyen rapide de vérifier si ces unités de code sont valides et en fait si ces paires de substitution peuvent représenter le caractère codé, ouvrez un DevTool de navigateur et entrez console.log('\uD83D\uDE0A');
dans la console.
Vous pouvez également utiliser cet outil en ligne pour générer des points de code UTF-16.
Plans de caractères Unicode
Un plan est un groupe continu de 21 points ou 65 536 points de code. Puisque UTF-16 a limité les points de code à un maximum de 10ffff₁₆, nous avons un total de 17 plans de caractères en standard Unicode à partir de 0 à 16.
Comme 21 caractères⁶ peuvent être définis par l’unité de code unique de UTF-16 (y compris les points de code de substitution), il forme le premier (0ème) plan. Cet avion contient presque tous les caractères dans les langues de base du monde entier. C’est pourquoi cet avion est appelé le plan multilingue de base ou BMP.
Ensuite, nous avons des points de code définis par deux unités de code de UTF-16. Ceux-ci contiennent 22 caractères⁰ car nous avons 20 bits pour coder la valeur du point de code. Ceux-ci sont divisés en 16 plans (2⁴ x 21⁶). Ce sont des plans supplémentaires.
For Pour plus d’informations sur ces avions, lisez ce document Wikipedia.
Comparaison avec UCS-2
UCS-2 est un codage à largeur fixe de 16 bits. Cela signifie qu’une seule unité de code 16 bits est utilisée pour représenter un point de code. En théorie, UCS-2 peut représenter 21 characters caractères distincts mais il y a une torsion.
B BTW, nous utilisons le terme unité de code dans ce codage à largeur fixe pour comprendre la relation entre UTF-16. En réalité, il n’existe pas d’unité de code dans un codage fixe.
Comme UCS suit le jeu de caractères Unicode, le codage des caractères dans UCS-2 est identique au codage des caractères en UTF-16 qui sont représentés dans une seule unité de code.
Since Comme UCS suit le jeu de caractères Unicode, il ne peut pas coder un caractère valide avec les points de code réservés aux substituts.
Donc en bref, UCS-2 contient les caractères du Plan multilingue de base. C’est la raison pour laquelle certains documents et logiciels plus anciens utilisaient l’encodage UCS-2. Mais l’encodage UCS-2 est devenu obsolète et UTF-16 est préféré.
Endianness et BOM
Comme nous en avons discuté précédemment, Un document codé en UTF à un niveau bas contient la séquence d’unités de code. Pour UTF-8, l’unité de code est longue de 8 bits tandis que pour UTF-16, elle est longue de 16 bits. Ces unités de code constituent les caractères.
Un décodeur UTF-8 ou UTF-16 lit séquentiellement les unités de code, une unité de code à la fois pour générer les caractères.
Chaque unité de code représente une valeur numérique qu’un décodeur UTF-8 ou UTF-16 peut examiner et décider s’il suffit de représenter un caractère ou s’il suit d’autres unités de code qui doivent également être considérées.
En ce qui concerne UTF-8, les choses sont simples. Étant donné que chaque unité de code a une longueur de 8 bits, la conversion de ce nombre binaire de 8 bits en une valeur numérique est rapide et facile. Ce n’est cependant pas le cas avec UTF-16.
L’unité de code UTF-16 est un nombre binaire de 16 bits (2 octets) qui représente une valeur de point de code. Générer la valeur numérique à partir de plusieurs octets, en général, est délicat et différents systèmes se comportent différemment.
Ce comportement dépend de l’endiannité du système. D’après notre discussion précédente sur l’endianness, il existe deux façons d’écrire une valeur unitaire de code UTF-16. Soit au format Big-endian, soit au format Little-endian.
Au format Big-endian, le MSB est stocké en premier et le LSB est stocké en dernier. Jusqu’à présent, nous écrivons la valeur unitaire du code UTF-16 au format Big-endian. Pour écrire une valeur unitaire de code UTF-16 en Little-endian, nous devons échanger des octets.
Parlons du caractère आ. De l’exemple précédent, il peut être représenté dans une seule unité de code de UTF-16 et son codage en représentation hexadécimale ressemble à 0906₁₆.
0906 is est un nombre de 16 bits avec 09 étant le MSB et 06 étant le LSB. Par conséquent, dans l’architecture Big-endian, il sera stocké sous la forme 09 06. Cependant, dans une architecture Little-endian, il sera stocké sous la forme 06 09.
Par conséquent, il devient de notre responsabilité d’encoder des caractères en prenant en compte l’endiannité du système afin que le système puisse lire correctement un document UTF-16.
Mais comment savoir à l’avance si la machine d’un utilisateur est compatible avec le document codé ou non ? Et puisque l’endianité d’un système peut affecter la façon dont un document est décodé, comment le partageons-nous publiquement?
C’est là que la nomenclature entre en jeu. Une marque d’ordre d’octets (BOM) est une séquence d’octets ajoutée au début d’un fichier texte ou d’une donnée texte.
Unicode recommande le caractère avec le point de code FEFF to pour agir comme nomenclature pour les encodages UTF-16 et UTF-32. Ce caractère doit précéder le premier caractère du document. Cependant, ce caractère ne sera pas pris en compte par le décodeur dans la sortie.
Ce caractère (U+FEFF) est un caractère d’espace sans rupture de largeur nulle (ZWNBSP) et il est invisible. Par conséquent, même si un décodeur ne reconnaît pas la nomenclature, il ne produira aucune sortie visible.
Ce caractère est représenté dans une seule unité de code UTF-16 et en représentation hexadécimale, il ressemble à FE(MSB) et FF(LSB).
Par conséquent, lorsque les caractères sont encodés au format Big-endian, nous devons ajouter FEFF comme nomenclature au début du fichier et lorsque les caractères sont encodés au format Little-endian, nous devons ajouter FFFE (inverse) comme nomenclature au début du fichier.
Unicode recommande d’ajouter la nomenclature au document encodé en UTF-16. Cependant, si la nomenclature est manquante, le format Big-endian est supposé.
L’IANA préfère UTF-16 comme identifiant pour signifier un document codé en UTF-16. Cependant, UTF-16BE est utilisé pour le document codé au format Big-endian et UTF-16LE est utilisé pour le format Little-endian.
Lorsque le nom UTF-16BE ou UTF-16LE est utilisé, il n’est pas recommandé d’ajouter la nomenclature à un fichier. Même dans ce cas, si la nomenclature est ajoutée, elle sera considérée comme un caractère ZWNBSP et elle ne sera pas ignorée.
UTLes noms UTF-16, UTF-16BE et UTF-16LE ne sont pas sensibles à la casse.
Avantages et inconvénients
UTF-16 est efficace car il ne comporte que 2 unités de code et comme la plupart des caractères utilisés appartiennent à l’ensemble BMP, ils peuvent être représentés dans une seule unité de code. Cependant, cela comporte de nombreux problèmes.
Le plus gros inconvénient de l’UTF-16 est qu’il n’est pas compatible ASCII. Étant donné que les caractères ASCII sont codés avec une seule unité de code (nombre de 16 bits), ils ne peuvent pas être décodés correctement par un décodeur ASCII.
UTF-16 consomme de l’espace inutile pour les caractères ASCII. Par rapport au document codé en UTF-8 qui ne contient que des caractères ASCII, la taille du même document codé en UTF-16 est deux fois plus grande.
UTF-16 est également affecté par l’endianité du système. Si la nomenclature est manquante et qu’un identifiant de codage approprié n’est pas utilisé (comme UTF-16LE), un document encodé en UTF-16 peut ne pas être décodé correctement.
En raison de la nature du codage UTF-16, il a introduit des points de code de substitution qui ne peuvent pas représenter les caractères valides. En outre, il a limité le jeu de caractères Unicode à 10ffff₁₆ (dernier point de code).
Malgré ces faits, certains des langages de programmation comme JavaScript, Java, etc. et les systèmes comme Windows préfèrent l’encodage UTF-16.