D'où viennent les mojibakes? Essentiels des encodages


Cet article explore les concepts de base du codage de caractères, puis approfondit les détails techniques des systèmes de codage.


Si vous avez juste une connaissance de base de l'encodage de caractères et que vous souhaitez mieux comprendre l'essentiel, les différences entre les systèmes d'encodage, pourquoi nous nous retrouvons parfois avec du texte absurde et les principes derrière différentes architectures de système d'encodage, alors lisez la suite.


Apprendre à comprendre l'encodage des caractères en détail nécessite une lecture approfondie et beaucoup de temps. J'ai essayé de vous épargner une partie de cet effort en rassemblant tout cela en un seul endroit tout en fournissant ce que je pense être un fond assez approfondi du sujet.


Je vais passer en revue le fonctionnement des codages à un octet (ASCII, Windows-1251, etc.), l'historique de la façon dont Unicode a vu le jour, les codages basés sur Unicode UTF-8, UTF-16 et leurs différences, le caractéristiques spécifiques, compatibilité et absence de fonctionnalités parmi les différents encodages, principes d'encodage des caractères et guide pratique sur la façon dont les caractères sont encodés et décodés.


Bien que l'encodage des caractères ne soit pas un sujet de pointe, il est utile de comprendre comment cela fonctionne maintenant et comment cela a fonctionné dans le passé sans passer beaucoup de temps.


Histoire de l'unicode


Je pense qu'il est préférable de commencer notre histoire à partir du moment où les ordinateurs n'étaient pas aussi avancés ni aussi banals qu'une partie de notre vie comme ils le sont maintenant. Les développeurs et les ingénieurs qui essayaient de trouver des normes à l'époque n'avaient aucune idée que les ordinateurs et Internet seraient aussi populaires et omniprésents qu'eux. Lorsque cela s'est produit, le monde avait besoin d'encodages de caractères.


Mais comment pouvez-vous avoir un ordinateur pour stocker des caractères ou des lettres alors qu'il ne comprend que les uns et les zéros? De ce besoin est né le premier codage ASCII à 1 octet qui, bien que n'étant pas nécessairement le premier codage, a été le plus largement utilisé et a établi la référence. C'est donc un bon standard à utiliser.


Mais qu'est-ce que l'ASCII? Le code ASCII est composé de 8 bits. Une arithmétique simple montre que ce jeu de caractères contient 256 symboles (huit bits, zéros et uns 2⁸ = 256).


Les 7 premiers bits - 128 symboles (2⁷ = 128) de l'ensemble ont été utilisés pour les lettres latines, les caractères de contrôle (tels que les sauts de ligne, les tabulations, etc.) et les symboles grammaticaux. Les autres éléments concernaient les langues nationales. De cette façon, les 128 premiers caractères sont toujours les mêmes, et si vous souhaitez encoder votre langue maternelle, servez-vous des symboles restants.


Cela a donné lieu à une panoplie d'encodages nationaux. Vous vous retrouvez avec une situation comme celle-ci: disons que vous êtes en Russie en train de créer un fichier texte qui, par défaut, utilisera Windows-1251 (l'encodage russe utilisé dans Windows). Et vous envoyez votre document à quelqu'un en dehors de la Russie, par exemple aux États-Unis. Même si le destinataire connaît le russe, il n'aura pas de chance lorsqu'il ouvrira le document sur son ordinateur (avec un logiciel de traitement de texte utilisant ASCII comme code par défaut) car il verra des caractères bizarres bizarres (mojibake) au lieu de lettres russes . Plus précisément, toutes les lettres anglaises apparaîtront très bien, car les 128 premiers symboles dans Windows-1251 et ASCII sont identiques, mais partout où il y a du texte russe, le logiciel de traitement de texte de notre destinataire utilisera le mauvais encodage à moins que l'utilisateur n'ait défini manuellement le bon caractère encodage.


Le problème avec les normes de code de caractère national est évident. Et finalement, ces codes nationaux ont commencé à se multiplier, Internet a commencé à exploser, et tout le monde voulait écrire dans sa langue nationale sans produire ces mojibakes indéchiffrables.


Il y avait deux options à ce stade - utiliser un encodage pour chaque pays ou créer une carte de caractères universelle pour représenter tous les personnages de la planète.


Une courte introduction sur ASCII


Cela peut sembler trop élémentaire, mais si nous voulons être approfondis, nous devons couvrir toutes les bases.



Il y a 3 groupes de colonnes dans la table ASCII:


  • la valeur décimale du caractère
  • la valeur hexadécimale du caractère
  • le glyphe du personnage lui-même

Disons que nous voulons encoder le mot «ok» en ASCII. La lettre «o» a une valeur décimale de 111 et 6F en hexadécimal. En binaire, ce serait - 01101111. La lettre «k» est la position 107 en décimal et 6B en hexadécimal, ou - 01101011 en binaire. Ainsi, le mot «OK» en ASCII ressemblerait à 01101111 01101011. Le processus de décodage serait le contraire. Nous commençons par 8 bits, les traduisons en codage décimal et nous nous retrouvons avec le numéro de caractère, et recherchons dans le tableau le symbole correspondant.


Unicode


De ce qui précède, il devrait être assez évident pourquoi une seule carte de personnage commune était nécessaire. Mais à quoi cela ressemblerait-il? La réponse est Unicode qui n'est en fait pas un encodage, mais un jeu de caractères. Il se compose de 1114112 positions ou points de code, dont la plupart sont encore vides, il est donc peu probable que l'ensemble doive être étendu.


La norme Unicode se compose de 17 avions avec 65 536 points de code chacun. Chaque avion contient un groupe de symboles. Le plan zéro est le plan multilingue de base où l'on trouve les caractères les plus couramment utilisés dans tous les alphabets modernes. Le deuxième avion contient des caractères de langues mortes. Il y a même deux avions réservés à un usage privé. La plupart des avions sont encore vides.


Unicode a des points de code de 0 à 10FFFF (en hexadécimal).


Les caractères sont encodés au format hexadécimal précédé d'un "U +". Ainsi, par exemple, le premier plan de base comprend les caractères U + 0000 à U + FFFF (0 à 65 535), et le bloc 17 contient U + 100000 à U + 10FFFF (1 048 576 à 1 114 111).


Alors maintenant, au lieu d'une ménagerie de nombreux encodages, nous avons un tableau complet qui encode tous les symboles et caractères dont nous pourrions avoir besoin. Mais ce n'est pas sans ses défauts. Alors que chaque caractère était précédemment codé d'un octet, il peut désormais être codé en utilisant différents nombres d'octets. Par exemple, vous n'aviez besoin que d'un octet pour coder toutes les lettres de l'alphabet anglais. Par exemple, la lettre latine «o» en Unicode est U + 006F. En d'autres termes, le même nombre qu'en ASCII - 6F en hexadécimal et 111 en binaire. Mais pour coder le symbole «U + 103D5» (le nombre persan «100»), nous avons besoin de 103D5 en hexadécimal et de 66 517 en décimal, et maintenant nous avons besoin de trois octets.


Cette complexité doit être traitée par de tels codages Unicode comme UTF-8 et UTF-16. Et plus loin, nous les examinerons.


Utf-8


UTF-8 est un codage Unicode du système de codage à largeur variable qui peut être utilisé pour afficher n'importe quel symbole Unicode.


Que voulons-nous dire lorsque nous parlons de largeur variable? Tout d'abord, nous devons comprendre que l'unité structurelle (atomique) dans le codage est un octet. Le codage à largeur variable signifie qu'un caractère peut être codé en utilisant différents nombres d'unités ou d'octets. Par exemple, les lettres latines sont codées avec un octet et les lettres cyrilliques avec deux.


Avant de poursuivre, un petit côté concernant la compatibilité entre ASCII et UTF.


Le fait que les lettres latines et les caractères de contrôle clés tels que les sauts de ligne, les tabulations, etc. contient un octet rend le codage UTF compatible avec ASCII. En d'autres termes, les caractères de script et de contrôle latins se trouvent dans les mêmes points de code exacts en ASCII et UTF et sont codés en utilisant un octet dans les deux, et sont donc rétrocompatibles.


Utilisons la lettre «o» de notre exemple ASCII plus tôt. Rappelons que sa position dans le tableau ASCII est 111, ou 01101111 en binaire. Dans la table Unicode, c'est U + 006F, ou 01101111. Et maintenant, comme UTF est un système de codage à largeur variable, «o» serait un octet. En d'autres termes, «o» serait représenté de la même manière dans les deux. Et la même chose pour les caractères 0 - 128. Donc, si votre document contient des lettres anglaises, vous ne remarquerez pas de différence si vous l'ouvrez en utilisant UTF-8, UTF-16 ou ASCII, et ne remarquerez une différence que si vous commencez à travailler avec des codages nationaux.


Voyons comment l'expression mixte anglais / russe «Hello world» apparaîtrait dans trois systèmes de codage différents: Windows-1251 (codage russe), ISO-8859-1 (système de codage pour les langues d'Europe occidentale), UTF-8 (Unicode) . Cet exemple est révélateur car nous avons une phrase dans deux langues différentes.



Voyons maintenant comment ces systèmes de codage fonctionnent et comment nous pouvons traduire une ligne de texte d'un codage à un autre, et ce qui se passe si les caractères ne s'affichent pas correctement, ou si nous ne pouvons tout simplement pas le faire en raison des différences dans les systèmes.


Supposons que notre phrase d'origine ait été écrite avec l'encodage Windows-1251. Lorsque nous regardons le tableau ci-dessus, nous pouvons voir en traduisant de décimal ou hexadécimal en décimal que nous obtenons le codage ci-dessous en binaire en utilisant Windows-1251.


01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000


Nous avons donc maintenant l'expression «Bonjour tout le monde» dans l'encodage Windows-1251.


Imaginez maintenant que nous avons un fichier texte mais nous ne savons pas dans quel système de codage le texte a été enregistré. Nous supposons qu'il est codé en ISO-8859-1 et l'ouvrons dans notre traitement de texte en utilisant ce système de codage. Comme nous l'avons vu plus tôt, certains caractères apparaissent très bien, car ils existent dans ce système de codage, et sont même dans les mêmes points de code, mais les caractères du mot russe "monde" ne fonctionnent pas aussi bien. Ces caractères n'existent pas dans le système de codage, et à leur place, ou points de code, dans ISO-8859-1, nous trouvons des caractères complètement différents. Donc, «m» est le point de code 236, «et» est 232, et «p» est 240. Mais dans ISO-8859-1, ces points de code correspondent à «ì» (236), «è» (232) et « ð ”(240).


Ainsi, notre phrase en langage mixte "Hello World" encodée dans Windows-1251 et lue dans ISO-8859-1 ressemblera à "Hello ìèð". Nous avons une compatibilité partielle et nous ne pouvons pas afficher correctement une phrase encodée dans un système dans l'autre, car les symboles dont nous avons besoin n'existent tout simplement pas dans le deuxième encodage.


Nous avons besoin d'un encodage Unicode - dans notre cas, nous utiliserons UTF-8 comme exemple. Nous avons déjà expliqué que les caractères peuvent prendre entre 1 et 4 octets en UTF-8, mais un autre avantage est que UTF, contrairement aux deux systèmes de codage antérieurs, n'est pas limité à 256 symboles, mais contient tous les symboles du jeu de caractères Unicode .


Cela fonctionne comme ceci: le premier bit de chaque caractère codé correspond non pas au glyphe ou au symbole lui-même, mais à un octet spécifique. Donc, si le premier bit est nul, nous savons que le symbole codé utilise un seul octet - ce qui rend l'ensemble rétrocompatible avec ASCII. Si nous regardons de près la table des symboles ASCII, nous voyons que les 128 premiers symboles (l'alphabet anglais, les caractères de contrôle et les signes de ponctuation) sont exprimés en binaire, ils commencent tous par une valeur binaire de 0 (notez que si vous traduisez des caractères en binaire en utilisant un convertisseur en ligne ou quelque chose de similaire, le premier bit de poids fort zéro peut être ignoré, ce qui peut être un peu déroutant).


01001000 - la valeur du premier bit est 0, donc 1 octet code 1 caractère -> "H".


01100101 - la valeur du premier bit est 0, donc 1 octet code 1 caractère-> "e".


Si la valeur du premier bit n'est pas nulle, le symbole sera codé sur plusieurs octets.


Un codage à deux octets aura 110 pour les trois premières valeurs binaires.


11010000 10111100 - les bits de marqueur sont 110 et 10, nous utilisons donc 2 octets pour coder 1 caractère. Dans ce cas, le deuxième octet commence toujours par «10.» Nous omettons donc les bits de contrôle (les bits de tête qui sont surlignés en rouge et vert) et regardons le reste du code (10000111100), et convertissons en hexadécimal (043) -> U + 043C qui nous donne le "m" russe en Unicode.


Les bits initiaux d'un caractère à trois octets sont 1110.


11101000 10000111 101010101 - nous additionnons tous les bits sauf les bits de contrôle et nous constatons que dans l'hex, nous avons 103B5, U + 103D5 - l'ancien nombre persan cent (10000001111010101).


Les codages de caractères à quatre octets commencent par les bits de tête 11110.


11110100 10001111 10111111 10111111 - U + 10FFFF qui est le dernier caractère disponible dans l'ensemble Unicode (1000011111111111111111111).


Maintenant, nous pouvons facilement écrire notre phrase multilingue en encodage UTF-8.


Utf-16


UTF-16 est un autre codage à largeur variable. La principale différence entre UTF-16 et UTF-8 est que UTF-16 utilise 2 octets (16 bits) par unité de code au lieu de 1 octet (8 bits). En d'autres termes, tout caractère Unicode codé en UTF-16 peut être de deux ou quatre octets. Pour garder les choses simples, je ferai référence à ces deux octets comme une unité de code. Ainsi, en UTF-16, n'importe quel caractère peut être représenté en utilisant une ou deux unités de code.


Commençons par les symboles codés à l'aide d'une unité de code. Nous pouvons facilement calculer qu'il y a 65 535 (216) caractères avec une unité de code, ce qui correspond complètement au plan multilingue de base d'Unicode. Tous les caractères de ce plan seront représentés par une unité de code (deux octets) en UTF-16.


Lettre latine «o» - 00000000 01101111.


Lettre cyrillique «M» - 00000100 00011100.


Examinons maintenant les caractères en dehors du plan multilingue de base. Ceux-ci nécessitent deux unités de code (4 octets) et sont codés de manière légèrement plus compliquée.


Tout d'abord, nous devons définir le concept d'une paire de substitution. Une paire de substitution est deux unités de code utilisées pour coder un seul caractère (totalisant 4 octets). Le jeu de caractères Unicode réserve une plage spéciale D800 à DFFF pour les paires de substitution. Cela signifie que lors de la conversion d'une paire de substitution en octets en hexadécimal, nous nous retrouvons avec un point de code dans cette plage qui est une paire de substitution plutôt qu'un caractère séparé.


Pour coder un symbole dans la plage 10000 - 10FFFF (c'est-à-dire les caractères qui nécessitent plus d'une unité de code), nous procédons comme suit:


  1. Soustrayez 10000 (hex) du point de code (il s'agit du point de code le plus bas dans la plage 10000 - 10FFFF).


  2. On se retrouve avec un nombre jusqu'à 20 bits pas supérieur à FFFF.


  3. Les 10 bits de poids fort avec lesquels nous nous retrouvons sont ajoutés au D800 (le point de code le plus bas de la plage de paires de substitution en Unicode).


  4. Les 10 bits suivants sont ajoutés au DC00 (également de la plage de paires de substitution).


  5. Ensuite, nous nous retrouvons avec 2 unités de code de substitution 16 bits, dont les 6 premiers bits définissent l'unité comme faisant partie d'une paire de substitution.


  6. Le dixième bit de chaque substitut définit l'ordre de la paire. Si nous avons un «1», c'est le substitut principal ou élevé, et si nous avons un «0», c'est le substitut final ou faible.



Cela aura un peu plus de sens lorsqu'il est illustré avec l'exemple ci-dessous.


Encodons puis décodons le nombre persan cent (U + 103D5):


  1. 103D5 - 10000 = 3D5.


  2. 3D5 = 0000000000 1111010101 (les 10 bits les plus élevés sont nuls, et lorsqu'ils sont convertis en hexadécimal, nous nous retrouvons avec «0» (les dix premiers) et 3D5 (les dix derniers)).


  3. 0 + D800 = D800 (1101100000000000) les 6 premiers bits nous indiquent que ce point de code se situe dans la plage de la paire de substitution, le dixième bit (à partir de la droite) a une valeur "0", il s'agit donc du substitut élevé.


  4. 3D5 + DC00 = DFD5 (1101111111010101) les 6 premiers bits nous indiquent que ce point de code se situe dans la plage de la paire de substitution, le dixième bit (à partir de la droite) est un "1", nous savons donc qu'il s'agit de la substitution la plus faible.


  5. Le caractère résultant codé en UTF-16 ressemble à - 1101100000000000 1101111111010101.



Décodons maintenant le personnage. Disons que nous avons le point de code suivant - 1101100000100010 1101111010001000:


  1. Nous convertissons en hexadécimal = D822 DE88 (les deux points de code tombent dans la plage de la paire de substitution, nous savons donc que nous avons affaire à une paire de substitution).


  2. 1101100000100010 - le dixième bit (à partir de la droite) est un "0", c'est donc le substitut élevé.


  3. 1101111010001000 - le dixième bit (à partir de la droite) est un "1", c'est donc le substitut bas.


  4. Nous ignorons les 6 bits identifiant cela comme un substitut et nous nous retrouvons avec 0000100010 1010001000 (8A88).


  5. Nous ajoutons 10000 (le point de code le plus bas dans la plage de substitution) 8A88 + 10000 = 18A88.


  6. Nous regardons la table Unicode pour U + 18A88 = Tangut Component-649.



Bravo à tous ceux qui ont lu jusqu'ici!


J'espère que cela a été instructif sans vous laisser trop ennuyé.


Vous pourriez également trouver utile:

Le jeu de caractères Unicode


Stratégies de localisation de contenu: basées sur IP ou sur navigateur


À propos du traducteur


Alconost est un fournisseur mondial de services de localisation pour les applications , les jeux , les vidéos et les sites Web dans plus de 70 langues. Nous proposons des traductions par des linguistes natifs, des tests linguistiques, un workflow basé sur le cloud, une localisation continue, une gestion de projet 24h / 24 et 7j / 7 et travaillons avec n'importe quel format de ressources de chaîne. Nous réalisons également des vidéos et des images publicitaires et éducatives, des teasers, des explications et des bandes-annonces pour Google Play et l'App Store.

Source: https://habr.com/ru/post/fr480688/


All Articles