python encodage texte

Identification

Infoforall

19 - Encodage des textes


Prérequis : Cette activité s'effectue normalement après avoir effectué celle de la partie Donnée sur l'intérêt de l'hexadécimal. Vous devez donc normalement comprendre le principe de l'ASCII et savoir exprimer un nombre en base 10 (décimal) ou en base 16 (hexadécimal).

Après cette activité, vous comprendrez les blagues de ce style :

Martine, couverture UTF-8
Martine écrit en UTF-8

Logiciel nécessaire pour l'activité : Python 3 : Thonny, IDLE ...

Evaluation ✎ : questions 04-05-06-09-10-11-13-18-20

1 - ASCII

Nous avons vu que les ordinateurs stockent uniquement des bits associés en octets :

  • un nombre entier est représenté par un ou plusieurs octets
  • un nombre à virgule est représenté par une suite d'octets
  • une image est représentée par une suite d'octets,
  • un texte est représenté par une suite d'octets.

On a donc besoin d'une table de conversion lorsqu'on veut écrire un texte ou qu'on veut le lire . Sans la technique de conversion, impossible de savoir les nombres qu'il faut écrire à la place du texte initial ou comment décoder les nombres qu'on est en train de lire !

La transformation caractère en nombre est ce qu'on nomme l'encodage.

La transformation nombre vers caractère est ce qu'on nomme le décodage.

Initialement, la mémoire des ordinateurs n'était pas très grande et les communications n'étaient pas très fiables.

La première table normalisée partagée à grande échelle est la table ASCII : American Standard Code for Information Interchange.

Principe de la table ASCII

Apparue dans les années 1960, la table ASCII est devenue le standard incontournable des 128 premiers caractères.

Elle est publiée pour la première fois en 1963 et est le résultat d'un groupe de travail. La table ASCII attribue des valeurs codifiées

  • à 32 caractères dits caractères de contrôle : passage à la ligne, acquittement (voir la partie TCP), début de fichier, fin de fichier...
  • aux lettres minuscules (a-z),
  • majuscules (A-F),
  • aux chiffres décimaux (0-9),
  • aux caractères de ponctuation,
  • à quelques symboles mathématiques ou non...

Jusqu'alors, chaque matériel avait sa propre table d'encodage/décodage... Changer de support impliquait donc de changer les codes à inscrire !

La taille choisie pour l'encodage des caractères était de 7 bits. On pouvait donc encoder 27, soit 128 caractères au total. Pas un de plus. Cela représentait une place mémoire non négligeable à l'époque : on était à quelques centaines de kilo-octets (ko) à l'époque. De quoi encoder un petit livre uniquement, ou une image de qualité très médiocre.

Un petit bout de la table ASCII

Caractère encodé En binaire (7 bits) En décimal En hexadécimal
A  100 0001  65 41
B  100 0010  66 42
C  100 0011  67 43

Sous cette forme de tableau avec une ligne par caractère, cela va donner 128 lignes. Cette représentation prendrait beaucoup de place.

On préfère représenter la correspondance Code/Caractère via un tableau à deux entrées.

Quelques alias du nom ASCII :

  • iso-ir-6
  • ANSI_X3.4-1968
  • ANSI_X3.4-1986
  • ISO_646.irv:1991
  • ISO646-US
  • US-ASCII
  • us
  • IBM367
  • cp367
  • csASCII

Les premières questions vous permettront de remettre vos connaissances sur les bases 2 et 12 à jour.

01° Montrer que 41 12 correspond bien à 65 10.

Montrer ensuite que  100 0001  correspond également à 65 10.

...CORRECTION...

Calculons la valeur du nombre en base 10.

41 12

= (4 * 16 + 1) 10

= (64 + 1) 10

= 65 10

On peut faire la même chose avec le binaire.

 100 0001  2

= (1 * 64 + 1 * 1) 10

= 65 10

02° En décomposant  0100 0001  en deux quartets, montrer que ce nombre s'écrit bien 41 12.

...CORRECTION...

 100 0001  peut s'écrire  0100  et  0001 

On voit immédiatement que le premier quartet correspond à 4 et le deuxième à 1.

Les communications n'étant pas très fiables à l'époque, le 8e bit servait de bit de contrôle lors des communications : un exemple possible à l'émission,

  • si le code du caractère comporte un nombre pair de bits à 1, on met le 8e à 0 : on a au total un nombre pair de bits à 1,
  • si le code du caractère comporte un nombre impair de bits à 1, on met le 8e à 1 : on a au total un nombre pair de bits à 1,

Voici ce qu'on devrait envoyer pour émettre un A, un B ou un C.

Caractère encodé En binaire (7 bits) Nombre de bits à 1
A  0100 0001  2 donc pair
B  0100 0010  2 donc pair
C  1100 0011  4 donc pair

L'ordinateur récepteur pouvait alors faire la même chose sur les 7 bits. En comparant à la valeur du 8e bit, on pouvait savoir si une erreur de transmission avait à priori eu lieu.

03° Expliquer si les codes de caractères ci-dessous, reçus par un ordinateur sont possiblement erronés.

 1100 0111 

 1100 1111 

...CORRECTION...

Le premier est invalide : il y 5 bits à 1. Une erreur de transmission est détéctée.

Le deuxième comporte 6 bits à 1. Un nombre pair. Soit la communication est bonne, soit il y a eu plusieurs erreurs en même temps !

Voici la table ASCII complète, en version hexadécimale. Vous pouvez visualiser la valeur décimale d'un caractère est stabilisant la souris au dessus du caractère.

Table ASCII en version hexadécimale
_0 _1 _2 _3 _4 _5 _6 _7 _8 _9 _A _B _C _D _E _F
0_ NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI _0
1_ DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 1_
2_ ! " # $ % & ' ( ) * + , - . / 2_
3_ 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 3_
4_ @ A B C D E F G H I J K L M N O 4_
5_ P Q R S T U V W X Y Z [ \ ] ^ _ 5_
6_ ` a b c d e f g h i j k l m n o 6_
7_ p q r s t u v w x y z { | } ~ DEL 7_
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F

✎ 04° Utiliser le tableau pour trouver le caractère qui correspond à la valeur 0A 16.

Avec quelle association de caractères d'échappement parvient-on à le symboliser en Python ?

  • \t ?
  • \n ?
  • \f ?

2 - Encodage et décodage avec Python

Voyons maintenant comment Python parvient à gérer ces histoires d'encodage.

Méthode des strings : encode

On peut créer une suite d'octets en Python en utilisant la méthode encode. Cette méthode attend au moins un string contenant le nom de l'encodage à utiliser en argument.

La méthode renvoie un objet de type bytes qui veut dire usuellement octets.

>>> octets = "ABCDE".encode('ascii') >>> type(octets) <class 'bytes'>

Python affiche ces objets comme des strings précédés d'un b minuscule.

>>> print(octets) b'ABCDE'

Si la valeur de l'octet correspond à un caractère ASCII imprimable, l'interpréteur va alors afficher le caractère plutôt que la valeur.

Pour voir qu'il s'agit bien d'octets (0 à 255), il faut les afficher un par un ou les transformer en tableau (nous utilisons l'objet list en Python).

Remarque : il faut appuyer deux fois sur ENTREE pour lancer la boucle FOR.

>>> tableau = list(octets) >>> tableau [65, 66, 67, 68, 69] >>> for octet in octets : print(octet) 65 66 67 68 69

✎ 05° Utiliser les instructions ci-dessous dans votre Shell. Répondre ensuite aux questions :

>>> octets = "AB\nCD".encode('ascii') >>> octets b'AB\nCD' >>> len(octets) 5 >>> list(octets) [65, 66, 10, 67, 68]

Questions :

  • Que représente \n ?
  • Par quel octet est-il encodé ?
  • Pourquoi le string 'ABC\nD' est-il alors encodé par 5 octets alors qu'on voit clairement 6 caractères ?

✎ 06° Utiliser Python (ou à la main, mais ça va être long) pour trouver la façon dont on encode la chaîne de caractères suivante qui ne comporte aucun accent :"Bonjour a tous, l'ASCII permet d'encoder beaucoup de caracteres mais pas tous : il s'agit d'un encodage cree par les americains et donc beaucoup de caracteres des langages europeens ou asiatiques ne sont absolument pas presents dans cette table. C'est pour cela que je vous demande de ne pas mettre d'accents dans les noms de variables en Python, meme si nous verrons que le langage Python sait les gerer. Mais ce n'est pas le cas de tous les langages de programmation."

Question supplémentaire : combien d'octets sont nécessaires en mémoire pour stocker ce texte en ASCII ? Pensez à utiliser la fonction native len : elle fonctionne aussi sur les objets bytes.

Essayons maintenant de faire la même chose mais avec un texte réel. Un texte avec des accents et autres caractères étranges pour un lecteur anglais ou américain.

>>> a = "Bonjour à tous, l'ASCII permet d'encoder beaucoup de caractères mais pas tous : il s'agit d'un encodage créé par les américains et donc beaucoup de caractères des langages européens ou asiatiques ne sont absolument pas présents dans cette table. C'est pour cela que je vous demande de ne pas mettre d'accents dans les noms de variables en Python, même si nous verrons que le langage Python sait les gérer. Mais ce n'est pas le cas de tous les langages de programmation."

Là, aucun problème. Python sait très bien géré les accents dans les chaînes de caractères. Nous verrons d'ailleurs pourquoi aujourd'hui. Passons à la suite : la création de la suite d'octets encodant en ASCII ce texte :

>>> b = a.encode('ascii') UnicodeEncodeError: 'ascii' codec can't encode character '\xe0' in position 8: ordinal not in range(128)

Et voilà : un beau message d'erreur. Visiblement le caractère d'index 8 pose problème.

>>> a[8] 'à'

Pourquoi ce caractère pose-t-il problème : simplement car il n'est pas dans la table ASCII. On aura donc du mal à trouver la valeur de l'octet à lui faire correspondre !

Avec la rapide augmentation des mémoires, l'encodage des caractères est passé de 7 bits à 8 bits.

Un bit en plus veut alors dire qu'on double le nombre de caractères disponibles : on est passé de 27 (128, de 0 à 127) à 28 (256, de 0 à 255) valeurs disponibles. Soit 128 nouvelles valeurs permettant d'encoder 128 nouveaux caractères.

Une révolution.

3 - Les tables ASCII étendues

Les tables cp sont les tables d'encodage "code page" créés par IBM sur les premiers ordinateurs personnels.

Pendant très lontemps, l'une des plus connue a été la page de code 437 utilisé par IBM pour le système DOS et ces consoles.

Voici les caractères 128 (en hexa 80 12) à 255 (en haxa FF 12) de cette table. La première partie n'est pas reproduite car ce sont les mêmes codes que l'ASCII. Le but étant d'être compatible sur les caractères 127 et moins.

_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x8_ÇüéâäàåçêëèïîìÄÅ0x8_
0x9_ÉæÆôöòûùÿÖÜ¢£¥ƒ0x9_
0xA_áíóúñѪº¿¬½¼¡«»0xA_
0xB_0xB_
0xC_0xC_
0xD_0xD_
0xE_αßΓπΣσµτΦΘΩδφε0xE_
0xF_±÷°·²¤¤¤0xF_
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F

Vous pourriez vous demander pourquoi ils ont placé autant de caractères graphiques ? Simplement car à l'époque les affichages étaient encore très basiques. Pas d'environnement graphique. Ces caractères permettaient de réaliser de jolis tableaux.

Ces tables ont permis de donner encore plus de créativité aux gens qui réalisaient (et réalisent encore) une forme d'art : l'ASCII-Art.

_________________________________________________ | _ _ ____ ____ ___ ___ | | __ _ _ __| |_ / \ / ___| / ___|_ _|_ _| | | / _` | '__| __| / _ \ \___ \| | | | | | | | | (_| | | | |_ / ___ \ ___) | |___ | | | | | | \__,_|_| \__|/_/ \_\____/ \____|___|___| | |_______________________________________________|
)) \||/ (( | @@___oo)) /\ /\ / (__,,,,| ) /^\) ^\/ _) ) /^\/ _) ) _ / / _) /\ )/\/ || | )_) < > |(,,) )__) || / \)___)\ | \____( )___) )___ \______(_______;;; __;;;
\|/(_)_(_)\|/ @~ (o.o) ~@ /___( * )___\ NA NA / `U' \ NANA ( . ) NA !!! `>---<' _\ /_
o _====| o ====|_====| _====| | ====| I-I-I-I-I _ _ _ _ _ _ _ _ _ _ _ _ ====| | | \ ` ' / I-I-I-I-I-I I-I-I-I-I-I | | | |. | \ ` '_/ \ ` '_/ | / \ | | /^\| |__ [],| [*] __| ^ / ^ \ ^ | [*]| _|______|_ |__ ,| / \ / `\ / \ | ===| <=-=-==-=-=> ___| ___ |__ / /=_=_=_=\ \ |, _| \__ _'_/ I_I__I_I__I_I (====(_________)___) |____| ___ |. _ | \-\--|-|--/-/ | | [*I__| I_I__I____I_I_I | _ | |[] '| | | __ . | \-\--|-|--/-/ |` '| |. ' | __| ___ |__ ___ |__ |---------| | [] | / \ [] .|_| |_| |_| |_| |_| |_| [] [] | | ` | (===) .|-=-=-=-=-=-=-=-=-=-=-| / \ |[] | | []|` [] | . . . . . . . . . |- (===) |` __| | []| ` |/////////\\\\\\\\\\ |__. |[] | /| | <===> ' ||||| |||| | [] <===>-I-I-I-I-I-I-I-I-| ` I-| \I/ 1-- ||||| Maze's |||| | . ' \i/ . | | _| | . _||||| FunHouse |||| | | ----' |` .| ../|',v,.,,,.|||||/_________\||||,/|.,,.Y,,..|\__

Pour pouvoir afficher correctement ces dessins sur d'autres supports, il faut impérativement utiliser des polices de caractères à chasse fixe, c'est à dire que chaque caractère doit avoir exactement la même largeur.

Sinon, l'image est déformée.

Les logiciels permettant d'éditer du code sont habituellement à chasse fixe.

07° Pour voir les encodages disponibles sur votre machine, vous pouvez lancer le code suivant.

1 2 3 4 5 6 7 8 9 10 11 12
import encodings def lister_les_encodages() : '''Fonction qui renvoie un tableau contenant les encodages disponibles''' les_encodages = [e for e in sorted(set(encodings.aliases.aliases.values()))] return les_encodages if __name__ == '__main__' : print('Liste des encodages disponibles') for element in lister_les_encodages() : print(f'- {element}')

Ce code utilise set, un ensemble. C'est une structure différente de list. Notamment, un ensemble est conçu pour ne pas contenir plusieurs fois des éléments identiques. Vous pouvez lancer le code en remplaçant set par list : vous allez voir qu'on obtient alors plusieurs fois les mêmes noms.

Vous devriez en trouver un beau paquet. Nous allons voir pourquoi il y en a autant...

A titre d'exemple, voici la liste obtenue avec sur mon système (en blanc : ceux dont nous allons parler aujourd'hui) :

Liste des encodages disponibles - ascii - base64_codec - big5 - big5hkscs - bz2_codec - cp037 - cp1026 - cp1125 - cp1140 - cp1250 - cp1251 - cp1252 - cp1253 - cp1254 - cp1255 - cp1256 - cp1257 - cp1258 - cp273 - cp424 - cp437 - cp500 - cp775 - cp850 - cp852 - cp855 - cp857 - cp858 - cp860 - cp861 - cp862 - cp863 - cp864 - cp865 - cp866 - cp869 - cp932 - cp949 - cp950 - euc_jis_2004 - euc_jisx0213 - euc_jp - euc_kr - gb18030 - gb2312 - gbk - hex_codec - hp_roman8 - hz - iso2022_jp - iso2022_jp_1 - iso2022_jp_2 - iso2022_jp_2004 - iso2022_jp_3 - iso2022_jp_ext - iso2022_kr - iso8859_10 - iso8859_11 - iso8859_13 - iso8859_14 - iso8859_15 - iso8859_16 - iso8859_2 - iso8859_3 - iso8859_4 - iso8859_5 - iso8859_6 - iso8859_7 - iso8859_8 - iso8859_9 - johab - koi8_r - kz1048 - latin_1 - mac_cyrillic - mac_greek - mac_iceland - mac_latin2 - mac_roman - mac_turkish - mbcs - ptcp154 - quopri_codec - rot_13 - shift_jis - shift_jis_2004 - shift_jisx0213 - tactis - tis_620 - utf_16 - utf_16_be - utf_16_le - utf_32 - utf_32_be - utf_32_le - utf_7 - utf_8 - uu_codec - zlib_codec

08° Lancer maintenant le code suivant qui vous donnera la façon d'encoder les caractères accentués en utilisant la table cp437.

1 2 3 4 5 6 7 8 9 10 11 12 13
texte = "Test avec é, des à et des è" octets = texte.encode('cp437') print('\nLe string de base') print(texte) print("\nLa suite d'octets") print(octets) print("\nLa suite d'octets sous forme de liste") for octet in octets : print(octet, end=' ')

Questions

  • En comparant les trois affichages, trouver l'encodage en hexadécimal et décimal des é avec cp437.
  • Idem pour le à
  • Idem pour le è

...CORRECTION...

Si votre code ne parvient pas à se lancer pour une raison ou une autre, voici le résultat de l'affichage :

Le string de base Test avec é, des à et des è La suite d'octets b'Test avec \x82, des \x85 et des \x8a' La suite d'octets sous forme de liste 84 101 115 116 32 97 118 101 99 32 130 44 32 100 101 115 32 133 32 101 116 32 100 101 115 32 138

Pour rappel, voici la première ligne de la table cp437 avec les caractères voulus en rouge.

_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x8_ÇüéâäàåçêëèïîìÄÅ0x8_

On voit donc que la façon étrange d'afficher les objets bytes a en réalité une raison d'être : on voit rapidement les caractères qui ne sont pas présents dans la table ASCII (les caractères de 0 à 127).

Le string de base Test avec é, des à et des è La suite d'octets b'Test avec \x82, des \x85 et des \x8a'

La présence de l'antislash suivi du x \x veut donc dire qu'il s'agit d'un code hexadécimal d'un caractère non-ASCII. Les valeurs sont donc supérieures ou égales à 128 en base 10 ou 80 en base 16.

On peut voir également les caractères non-ASCII dans le deuxième cas : il suffit de chercher les valeurs supérieures à 127.

Le string de base Test avec é, des à et des è La suite d'octets sous forme de liste 84 101 115 116 32 97 118 101 99 32 130 44 32 100 101 115 32 133 32 101 116 32 100 101 115 32 138

Si on résume dans une table :

Caractère encodé En binaire (8 bits) En décimal En hexadécimal
é  1000 0010  130 82
à  1000 0101  133 85
é  1000 1010  138 8a

Pendant longtemps, il y a donc eu une multitude de tables : des tables pour ceux qui voulaient plutôt des caractères graphiques, des tables pour les pays d'Europe de l'Ouest, des tables pour les pays d'Europe du Nord, des tables pour le cyrillique, des tables pour les caractères asiatiques spécifiques, et même des tables par constructeurs ou par systèmes d'exploitation !

Bref, il n'était pas toujours facile de parvenir à décoder correctement un texte si on ne connaissait pas l'encodage qui avait été utilisé.

Table latin-1

L'une des autres tables très utilisée en Europe est la table iso8859_1, de son petit nom latin-1.

Elle a été l'encodage par défaut pendant de nombreuses années, sans être un standard contrairement à ASCII. Malgré ce status non-officiel, cette table a été la base pour créer UNICODE.

Elle comporte tous les caractères usuels pour écrire un texte dans l'une des langues de l'Europe de l'Ouest et reste entièrement compatible avec l'ASCII sur les caractères de valeurs inférieures à 128.

Remarquez bien qu'elle n'inclut pas de caractères graphiques : les applications graphiques sont apparues. Plus besoin de réaliser de tableaux ou de dessins sur la console en utilisant des caractères, on utilisait déjà à cette époque de vraies images par exemple.

Encore une fois, je ne mets pas les caractères 127 et moins : ce sont les mêmes valeurs que la table ASCII, de façon à garder une retro-compatibilité.

Remarquez bien que certaines valeurs ne sont pas utilisées. Certaines sont des caractères de contrôle.

_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x8_ PAD HOP BPH NBH IND NEL SSA ESA HTS HTJ LTSPLD PLU RI SS2SS30x8_
0x9_ DCS PU1 PU2 STS CCH MW SPA EPA SOS SGCI SCI CSI ST OSC PM APC0x9_
0xA_NBSP ¡¢£¤¥¦§¨©ª«¬SHY®¯0xA_
0xB_°±²³´µ·¸¹º»¼½¾¿0xB_
0xC_ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ0xC_
0xD_ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß0xD_
0xE_àáâãäåæçèéêëìíîï0xE_
0xF_ðñòóôõö÷øùúûüýþÿ0xF_
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F

Voici les alias courants de cette table :

  • ISO_8859-1:1987
  • ISO_8859-1
  • ISO-8859-1
  • iso-ir-100
  • csISOLatin1
  • latin1
  • l1
  • IBM819
  • CP819

Sachez que j'ai parlé ici de la table validée par l'IANA (Internet_Assigned_Numbers_Authority). Il existe également une version de cette table sans les caractères de contrôle.

✎ 09° Trois questions.

  • 1 - En vous inspirant des codes précédents, fournir les octets permettant d'encoder ce texte en latin-1 puis en utilisant cp437.
  • Le texte : "Perceval ne sait vraiment pas écrire en latin !".

  • 2 - Quel est le seul caractère dont l'encodage est différent sur les deux tables ici ?
  • 3 - Conclure sur les problèmes que peut provoquer la non-connaissance de l'encodage utilisé à la création pour ouvrir un fichier texte en lecture.

Pensez à alors voir votre liste d'encodages obtenue à la question 07 si votre programme dit qu'il ne connait pas l'un de ces encodages.

Je n'ai présenté ici qu'une toute petite partie des tables d'encodage qui étaient (et sont encore) disponibles. Plus d'exemple dans la fiche présentée en fin d'activité.

Sachez néanmoins que la table latin-1 a connu une mise à jour importante pour l'Europe : la table latin-9 (également nommée iso8859_15, et oui, c'est pas le même numéro...) inclut en effet un caractère qui n'existait pas au moment de la création de la première : le signe de l'Euro, la monnaie.

Latin-9, une sorte de mise à jour de latin-1

En rose, les associations valeurs-caractères qui ont changé par rapport à latin-1.

_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x8_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ 0x8_
0x9_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ 0x9_
0xA_¤¤¤¡¢£¥Š§š©ª«¬¤¤¤®¯0xA_
0xB_°±²³Žµ·ž¹º»ŒœŸ¿0xB_
0xC_ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ0xC_
0xD_ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß0xD_
0xE_àáâãäåæçèéêëìíîï0xE_
0xF_ðñòóôõö÷øùúûüýþÿ0xF_
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F

Un dernier exemple pour la route, l'iso-8859-11 ou Thai. Les 128 premières valeurs sont celles de l'ASCII, c'est à partir de 128 que ça diffère. Il y a même 8 valeurs non utilisées.

...iso-8859-11...

_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x8_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤0x8_
0x9_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤0x9_
0xA_¤¤¤0xA_
0xB_0xB_
0xC_0xC_
0xD_฿0xD_
0xE_0xE_
0xF_0xF_
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F

Si on résume, il existe une multitude de tables d'encodages sur un octet permettant d'écrire dans toutes les langues.

Pourquoi "encodages sur un octet" ? Tout simplement car toutes ces tables contiennent 256 associations valeurs-caractères qui permettent d'encoder un texte en prenant exactement 1 octet par caractère.

En réalité, toutes ces tables ne différent la plupart du temps que sur les valeurs 128 et plus. Elles gardent un compatibilité quasi-totale avec ASCII et proposent 128 caractères différents ensuite.

On notera d'ailleurs que l'espace est un caractère qui possède une valeur (32 10 ou 20 16) et nécessite donc un octet en mémoire.

Idem pour le passage à la ligne : le fameux caractère de contrôle qui j'avais représenté par une sorte de flèche-rouge lors de la partie sur la description des requêtes GET / POST :

GET /act/archi/communication-client-serveur/ HTTP/1.1
Host: www.infoforall.fr

User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0

Il s'agit du caractère LF, LINE FEED, \n dans les strings de Python. Il possède une valeur 10 10 ou 0A 16. Et il représente un octet à chaque fois qu'il est présent. Comme n'importe quel autre caractère.

Vous pouvez être quasiment certain de ces deux valeurs d'octets lorsque vous les rencontrez dans un fichier-texte.

✎ 10° Estimer la taille en octet nécessaire pour encoder ce texte en latin-1. Pensez bien à tous les caractères, même les caractères "cachés".

Bon, alors combien d'octets pour stocker ça ?

Avec ce principe, on peut donc écrire un texte en anglais, un texte en français (en prenant une table contenant les caractères voulus), un texte en chinois (en prenant une table contenant les bons caractères), un texte en russe (en prenant une table contenant les bons caractères)...

Deux problèmes néanmoins qui vont justifier l'invention d'UNICODE :

  • Si on ne connait pas la table ayant réalisé le codage, difficile de parvenir à faire le décodage vu le nombre de tables disponibles...
  • Si on veut écrire un texte dans plusieurs langues sur un même support (comme un site Web), ben, c'est pas possible...

4 - Encodage et décodage

Nous avons vu comment encoder avec la méthode encode. Il est temps de parvenir à savoir décoder avec la méthode ... decode.

Méthode des bytes : decode

On peut créer un string en utilisant sur un objet bytes la méthode decode. Cette méthode attend au moins un string contenant le nom de la table d'encodage à utiliser en argument.

La méthode renvoie un objet de type str.

Un exemple avec un texte qu'on avait encodé en latin-9, nommé plutôt iso8859_15 sur ma machine. Voir la question 07 pour voir le nom qu'elle peut porter sur la votre.

>>> texte = b"Cet article co\xFBte\x2020\xA3.".decode('iso8859_15') >>> texte 'Cet article coûte 20£.'

Pourquoi un tel résultat ? Il suffit d'aller voir la valeur des caractères en hexadécimal pour ceux précédé de \x.

  • \xFB pour û.
  • \x20 pour   (espace).
  • \xA3 pour £.

✎ 11° Que va contenir texte dans l'exemple ci-dessous ?

Attention, il y a une différence par rapport à l'exemple du dessus.

>>> texte = b"Cet article co\xFBte\x2020\xA4.".decode('iso8859_15') >>> texte ? votre réponse ?

Là où c'était vraiment pénible, c'était donc lorsqu'on ne connaissait pas l'encode d'un texte. On devait faire au hasard, en utilisant l'encodage le plus probable.

Voici ainsi un exemple où on encode un texte en utilisant une table d'encodage précise et où on tente de décoder ce même texte en utilisant des tables au hasard...

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
texte = "Ce n'est pas facile, et même plutôt compliqué de décoder une table contenant des caractères étranges, ou des valeurs comment des $, des £ ou des €." octets = texte.encode('iso8859_15') # La suite brute des octets for octet in octets : print(octet, end=" ") # Tentative d'ouverture en latin-1 print('\nEn utilisant latin-1') print(octets.decode('latin-1')) # Tentative d'ouverture en iso8859_2 (Europe centrale) print('\nEn utilisant iso8859_2') print(octets.decode('iso8859_2')) # Tentative d'ouverture en iso8859_3 (Europe du Sud) print('\nEn utilisant iso8859_3')

Et voici les réponses fournies par Python :

67 101 32 110 39 101 115 116 32 112 97 115 32 102 97 99 105 108 101 44 32 101 116 32 109 234 109 101 32 112 108 117 116 244 116 32 99 111 109 112 108 105 113 117 233 32 100 101 32 100 233 99 111 100 101 114 32 117 110 101 32 116 97 98 108 101 32 99 111 110 116 101 110 97 110 116 32 100 101 115 32 99 97 114 97 99 116 232 114 101 115 32 233 116 114 97 110 103 101 115 44 32 111 117 32 100 101 115 32 118 97 108 101 117 114 115 32 99 111 109 109 101 110 116 32 100 101 115 32 36 44 32 100 101 115 32 163 32 111 117 32 100 101 115 32 164 46 En utilisant latin-1 Ce n'est pas facile, et même plutôt compliqué de décoder une table contenant des caractères étranges, ou des valeurs comment des $, des £ ou des ¤. En utilisant iso8859_2 Ce n'est pas facile, et męme plutôt compliqué de décoder une table contenant des caractčres étranges, ou des valeurs comment des $, des Ł ou des ¤. En utilisant iso8859_3 Ce n'est pas facile, et même plutôt compliqué de décoder une table contenant des caractères étranges, ou des valeurs comment des $, des £ ou des ¤. En utilisant iso8859_11 Ce n'est pas facile, et m๊me plutt compliqu้de d้coder une table contenant des caract่restranges, ou des valeurs comment des $, des ou des .

Comme on le voit, parfois ça marche, parfois certains caractères ne sont pas les bons. Bref, ce n'est pas possible en tant qu'outil professionnel d'édition.

D'où l'invention d'un nouveau système plus flexible que des tables de 256 caractères.

5 - UNICODE

Tant que les ordinateurs ne communiquaient pas entre eux, ou qu'ils le faisaient dans la même langue, cela ne posait pas (trop) de problème. Mais avec l'apparition de la communication globale via Internet, un langage de programmation pouvait fonctionner avec un certain encodage à l'interne (par exemple cp1252) et créer un fichier-texte encodé en latin-3. Ensuite, ce fichier pouvait être lu en Turquie par quelqu'un ayant un encodage de type mac-turkish. Bref, les risques d'avoir des affichages bizarres (au mieux), des programmes qui plantent ou des bases de données complétement anéanties (le pire qui soit) devenait non négligeable.

UNICODE

UNICODE n'est pas une table d'encodage à proprement parler.

Il s'agit juste d'une correspondance entre :

  • Un numéro unique attribué au caractère
  • Le glyphe du caractère
  • Le nom descriptif du caractère

A la charge de système d'encodage adapté de faire la correspondance. UNICODE ne se charge pas de la technique. C'est uniquement une correspondance entre un numéro et un caractère.

Les encodages UTF-8, UTF-16, UTF-32 se chargent de l'aspect technique justement.

Logo UNICODE
Logo UNICODE - Image dans le domaine public

Actuellement, UNICODE est une vaste collection qui contient tous les caractères des langues connues, les caractères scientifiques, des éléments graphiques mais également des icones comme les emote-icons.

Pour maintenir une retro-compatibilité, les valeurs UNICODE des valeurs 0 à 255 correspondent exactement aux valeurs décimales de la table d'encodage sur un octet latin-1. Cela veut dire également que les valeurs 0 à 127 correspondent exactement aux valeurs décimales de la table ASCII.

Les caractères présents sur les autres tables d'un octet sont présents dans UNICODE mais à des valeurs supérieures à 256.

Plus de renseignements et une visualisation de tous les caractères, sur le site d'UNICIDE.

La page correspondante de Wikipedia est très bien réalisée également WIKIPEDIA.

Voici quelques minces extraits des caractères présents dans UNICODE. Pour visualiser le numéro en décimal, il suffit de rester stationnaire au dessus du caractère.

...0 à 255 : identique aux valeurs latin-1...

Numéros 0-255
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x0_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
0x1_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
0x2_ !"#$%&'()*+,-./
0x3_0123456789:;<=>?
0x4_@ABCDEFGHIJKLMNO
0x5_PQRSTUVWXYZ[\]^_
0x6_`abcdefghijklmno
0x7_pqrstuvwxyz{|}~¤¤¤
0x8_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
0x9_¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
0xA_¤¤¤¡¢£¤¥¦§¨©ª«¬¤¤¤®¯
0xB_°±²³´µ·¸¹º»¼½¾¿
0xC_ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
0xD_ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
0xE_àáâãäåæçèéêëìíîï
0xF_ðñòóôõö÷øùúûüýþÿ

...Numéros 256-511...

Numéros 256-511
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x10_ĀāĂ㥹ĆćĈĉĊċČčĎď
0x11_ĐđĒēĔĕĖėĘęĚěĜĝĞğ
0x12_ĠġĢģĤĥĦħĨĩĪīĬĭĮį
0x13_İıIJijĴĵĶķĸĹĺĻļĽľĿ
0x14_ŀŁłŃńŅņŇňʼnŊŋŌōŎŏ
0x15_ŐőŒœŔŕŖŗŘřŚśŜŝŞş
0x16_ŠšŢţŤťŦŧŨũŪūŬŭŮů
0x17_ŰűŲųŴŵŶŷŸŹźŻżŽžſ
0x18_ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏ
0x19_ƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟ
0x1A_ƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯ
0x1B_ưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿ
0x1C_ǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏ
0x1D_ǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟ
0x1E_ǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯ
0x1F_ǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ

...Numéros 512-767...

Numéros 512-767
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x20_ȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏ
0x21_ȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
0x22_ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯ
0x23_ȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿ
0x24_ɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏ
0x25_ɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟ
0x26_ɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
0x27_ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ
0x28_ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏ
0x29_ʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟ
0x2A_ʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯ
0x2B_ʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿ
0x2C_ˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏ
0x2D_ːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟
0x2E_ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯
0x2F_˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿

...Numéros 8704-8959...

Numéros 8704-8959
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0x220_
0x221_
0x222_
0x223_
0x224_
0x225_
0x226_
0x227_
0x228_
0x229_
0x22A_
0x22B_
0x22C_
0x22D_
0x22E_
0x22F_

...Numéros 63744-63999...

Numéros 63744-63999
_0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F
0xF90_
0xF91_
0xF92_
0xF93_錄
0xF94_
0xF95_
0xF96_
0xF97_勵
0xF98_
0xF99_
0xF9A_
0xF9B_樂
0xF9C_
0xF9D_
0xF9E_
0xF9F_刺

Au moment où j'ai écrit cette partie, nous en étions à Unicode 12.0, version qui contient 137 928 caractères différents. Attention néanmoins, ils ne sont pas juste classés dans l'ordre.

Les caractères UNICODE sont classés en bloc thématique contenant 65536 caractères possibles. Il existe donc beaucoup de valeurs non utilisées puisque les blocs ne sont pas totalement plein loin de là.

Le but est de garantir une durée de vie énorme au système en pouvant rajouter un nombre très important de valeurs.

Justement, Python utilise directement à l'interne ce code pour gérer ces caractères. C'est pour cela que vous pourriez utiliser des noms de variables ou des fonctions en utlisant les caractères de n'importe quelle langue.

Fonctions natives ord et chr

La fonction native chr renvoie le caractère UNICODE correspondant au nombre décimal décimal fourni en argument.

>>> chr(65) 'A' >>> chr(10000) '✐' >>> chr(20000) '丠'

La fonction native ord fait l'inverse : elle renvoie la valeur UNICODE correspondant au caractère fourni en argument.

>>> ord('A') 65 >>> ord('✐') 10000 >>> ord('丠') 20000

Du coup, si vous cherchez la correspondance entre un caractère et une valeur ASCII ou latin-1, ça marche aussi puisque les valeurs 0-255 d'UNICODE sont compatibles avec ces tables.

12° Créer un programme qui permet d'afficher les caractères UNICODE de l'invervalle [1000;1100].

...CORRECTION...

J'espère que vous avez trouvé tout seul...

Une correction pour la forme.

1 2
for valeur in range(1000,1101) : print(chr(valeur))

Et le résultat attendu :

Ϩ ϩ Ϫ ϫ Ϭ ϭ Ϯ ϯ ϰ ϱ ϲ ϳ ϴ ϵ ϶ Ϸ ϸ Ϲ Ϻ ϻ ϼ Ͻ Ͼ Ͽ Ѐ Ё Ђ Ѓ Є Ѕ І Ї Ј Љ Њ Ћ Ќ Ѝ Ў Џ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь

C'est bien gentil tout ça mais ça n'explique pas concrétement comment encoder les caractères.

✎ 13° Fournir un calcul montrant qu'on peut encoder plus de 4 milliards de caractères avec 4 octets.

En nous arrivons au problème majeur de l'UNICODE : comment parvenir à encoder correctement tous ces caractères.

Imaginons qu'on décide d'utiliser 4 octets pour chaque caractère. Cela permet d'aller jusqu'à 4 milliards de caractères, nous avons donc plus de marge qu'avec les tables d'un octet...

Cet encodage brute se nomme UTF-32.

D'où vient ce nom ? De Universal Character Set Transformation Format 32 bits. Ca tombe bien 32 bits, ça donne 4 octets.

Le problème d'UTF-32 vient de la taille des fichiers : il serait 4 fois plus lourd à transporter ou à stocker qu'avec les encodages 1 octet, puisqu'il faudrait 4 octets par caractère.

Un fichier-texte de 100 000 caractères encodé en latin-1 prendrait 100 ko de place mémoire sans compression.

Le même fichier encodé avec UTF-32 occuperait 400 ko.

Et c'est qu'intervient le fameux UTF-8.

6 - UTF-8

L'idée derrière UTF-8 est de pouvoir utiliser les valeurs UNICODE en prenant moins que 4 octets par caractère.

Il est aujourd'hui l'encodage par défaut des systèmes UNIX compatibles (Linux, MacOS) et plus de 95% des pages Web sont encodées en UTF-8.

Nous n'allons pas rentrer dans le détail de la norme ici (ce n'est pas au programme et il s'agit juste d'une technique de codification qui pourrait être aménée à changer ou disparaître).

Perceval et l'UTF-8

L'idée est de parvenir à encoder les caractères courants sur un octet, les caractères moins courants sur 2 octets et les autres caractères sur 3 ou 4 octets.

L'idée générale est d'abord que les caractères courants dans la plupart des langages occidentaux (ceux de la table ASCII donc) soient encodés sur 1 octet.

Caractères courants (dans notre culture occidentale)

Pour ces caractères ASCII, on utilise uniquement 7 bits pour le stockage de l'information.

Si le bit de poids fort est à  0 , cela indique que le caractère est encodé sur un seul octet :

Pour ce caractère, on aura toujours en UTF-8 un contenu binaire de la forme  :  0xxx xxxx  x  représente un bit quelconque.

Il y a 7  x  : on peut coder sur 7 bits, donc toutes les valeurs de la table ASCII.

Exemple : le code ASCII du A est 65 en décimal et en binaire  100 0001 .

Son code UTF-8 est donc obtenu en rajoutant  0  devant :  0100 0001 .

Il correspond donc toujours à un octet donnant 65 en décimal. C'est un A.

14° Quelle place mémoire prendrait le texte suivant en étant encodé en ASCII, en UTF-8 puis en UTF-32 ?

>>> texte = "ABCDE"

...CORRECTION...

Il ne s'agit que de caractères ASCII.

Il y a 5 caractères.

En ASCII : 5 octets.

En UTF-8 : 5 octets.

En UTF-32 : 20 octets.

15° Voici un exemple de code permettant de trouver l'encodage UTF-8 d'une chaîne de caractères purement ASCII ici. Remarquez bien que tous les bits commencent à 0.

1 2 3 4 5 6 7
texte = "ABCDE" octets = texte.encode('utf-8') suite_en_decimal = list(octets) suite_en_binaire = [bin(octet)[2:].rjust(8,'0') for octet in octets] print(suite_en_decimal) print(suite_en_binaire)

Vous devriez trouver

[65, 66, 67, 68, 69] ['01000001', '01000010', '01000011', '01000100', '01000101']

On obtient bien 4 octets.

16° Relancer en changeant juste l'encodage pour obtenir de l'UTF-32.

1 2 3 4 5 6 7
texte = "ABCDE" octets = texte.encode('utf-32') suite_en_decimal = list(octets) suite_en_binaire = [bin(octet)[2:].rjust(8,'0') for octet in octets] print(suite_en_decimal) print(suite_en_binaire)

Vous devriez trouver

[255, 254, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 0] ['11111111', '11111110', '00000000', '00000000', '01000001', '00000000', '00000000', '00000000', '01000010', '00000000', '00000000', '00000000', '01000011', '00000000', '00000000', '00000000', '01000100', '00000000', '00000000', '00000000', '01000101', '00000000', '00000000', '00000000']

Ah zut. Nous obtenons 24 octets et pas 20...

Culture générale, hors NSI

En réalité, les 4 premiers octets sont ce qu'on nomme le BOM : byte order mark. Par convention, le BOM contient 255 et 254. Les placer permet au système de savoir dans quel ordre il doit placer les octets, si on fonctionne en BIG ENDIAN ou LITTLE ENDIAN.

Une chaîne UTF-32 comporte donc toujours 4 octets supplémentaires en début de sa suite d'octets.

Si le système lit 254 - 255 - 0 - 0 : on lui indique qu'il doit lire la suite en BIG ENDIAN.

Si le système lit 255 - 254 - 0 - 0 : on lui indique qu'il doit lire la suite en LITTLE ENDIAN.

Si vous voulez comprendre un peu plus, lisez la suite. Sinon, vous pouvez sortir de la boîte rouge.

...suite...

Ici, nous sommes donc en LITTLE ENDIAN puisqu'on reçoit 255 puis 254. C'est pour cela que les 4 octets bleus sont décodés de cette façon : 65 * 1 + 0 * 28 + 0 * 216 + 0 * 224 = 65.

Si nous avions été en BIG ENDIAN, cela aurait donné : 65 * 224 + 0 * 216 + 0 * 28 + 0 * 1 = 1090519040.

Passons maintenant aux caractères moins courants.

Caractères moins courants (dans notre culture occidentale)

Si le bit de poids fort du premier octet lu est à  1 , cela veut dire qu'il faudra lire plusieurs octets pour trouver la valeur UNICODE du caractère.
Le nombre de bits à  1  avant de rencontrer un premier bit à  0  donne le nombre d'octets à lire pour décoder le caractère.

 110x xxxx  veut dire qu'il faut tenir compte de cet octet et du suivant également.

 1110 xxxx  veut dire qu'il faut tenir compte de cet octet et des deux suivants.

 1111 0xxx  veut dire qu'il faut tenir compte de cet octet et des trois suivants.

Pour les octets suivants justement, l'encodage impose que leurs deux bits de poids forts soient 10 : ils auront donc tous une structure du type  10xx xxxx .

Code sur 2 octets :

  •  110x xxxx  -  10xx xxxx 
  • Cela veut dire qu'il y a 2 octets à lire en tout.
  • Il y a 11  x  : on peut coder sur 11 bits.
  • Ces 211 valeurs possibles vont bien entendu être utilisées pour les valeurs assez courantes dans les langages occidentaux. Les lettres accentuées, ect...

    Exemple : le "é" est représenté par les deux octets  195   169 . En binaire, cela donne
     1100 0011   1010 1001 .

Code sur 3 octets :

  •  1110 xxxx  -  10xx xxxx  -  10xx xxxx 
  • Cela veut dire qu'il y a 3 octets à lire en tout.
  • Il y a 16  x  : on peut coder sur 16 bits.
  • Ici, vont se trouver les caractères vraiment pas courants.

    Exemple : le "✐" est représenté par les trois octets  226   156   144 . En binaire, cela donne
     1110 0010   1001 1100   1001 0000  .

Code sur 4 octets :

  •  11110xxx  -  10xx xxxx  -  10xx xxxx  -  10xx xxxx 
  • Cela veut dire qu'il y a 4 octets à lire en tout.
  • Il y a 21  x  : on peut coder sur 21 bits.
  • Ce sont les caractères encore moins courants que les précédents.

17° Quel est le problème de l'UTF-8 pour les langages qui n'utilisent pas du tout les caractères usuels ASCII ?

...CORRECTION...

Pour ces langages, UTF-8 n'est pas spécialement performant puisque les caractères encodés sur 1 octet ne seront pas utilisés. De plus, la majorité de leurs caractères risquent de se retrouver dans la catégorie 3 OCTETS.

Ainsi, un texte asiatique a toute les chances de peser 3 fois plus lourd qu'un texte anglais.

UTF-16

L'encodage UTF-16 est utilisé notamment dans les pays asiatiques. Tous les caractères sont encodés sur 2 octets ou 4 octets. Mais leurs caractères spécifiques sont présents dans la zone des 2 OCTETS. Cette encodage est donc plus performant que l'UTF-8 sur ce genre de langage.

Il nous reste à avoir une explication sur l'image d'introduction.

Martine, couverture UTF-8
Martine écrit en UTF-8

Et ça marche aussi avec Perceval :

Perceval et l'UTF-8

Ce genre d'erreur arrivait assez couramment sur le Web à l'époque de la montée en puissance de l'UTF-8 alors que latin-1 était encore très présent.

Un Webmaster publiait ces pages HTML en UTF-8 (sans préciser son encodage dans sa page, dans la balise HEAD) et le navigateur du client avait lui latin-1 comme encodage par défaut s'il n'avait aucune indication de l'encodage à utiliser.

✎ 18° Utiliser le programme ci-dessous pour visualiser la mauvaise "traduction". Expliquer ensuite le plus clairement possible pourquoi le "é" se transforme en deux caractères "é".

1 2 3 4 5 6 7 8 9 10 11 12 13 14
encodage_depart = 'utf-8' encodage_recepteur = 'latin-1' texte_origine = "Martine écrit en " + encodage_depart print("1) On a un texte d'origine") print(texte_origine) octets = texte_origine.encode(encodage_depart) print(f'\n2) On encode le texte en {encodage_depart} et on envoie des octets') print(octets) texte_decode = octets.decode(encodage_recepteur) print('\n3) On recoit les octets mais on décode avec un autre encodage') print(texte_decode)

Ca marche aussi avec Perceval bien entendu :

1) On a un texte d'origine C'est écrit en utf-8? 2) On encode le texte en utf-8 et on envoie des octets b"C'est \xc3\xa9crit en utf-8?" 3) On recoit les octets mais on décode avec un autre encodage C'est écrit en utf-8?

19° Réaliser maintenant l'encodage en latin-1. Tenter de lire les octets en utf-8. Quel est le problème ?

...CORRECTION...

Cette fois, cela provoque une erreur car toutes les valeurs d'octets ne sont pas autorisées en UTF-8. Souvenez-vous de l'histoire des 1 au début du premier octet et des suivants qui doivent commencer par 10... Si les octets reçus ne suivent pas cette logique, le programme ne va pas réussir à décoder, même mal le texte.

Or ici, le "é" encodé en latin-1 est un octet qui commence bien par un 1 puisqu'il est supérieur à 127. Mais l'octet suivant ne commence pas par 10. Et ça ne va pas...

Les QCM pour savoir ce qu'il faut retenir pour la fin.

✎ 20° Répondre aux questions suivantes :

Quel a été le premier encodage standardisé des caractères ?

  • A : ASCII
  • B : cp-437
  • C : latin-1 ou iso8859_1
  • D : UTF-8

Combien d'associations valeurs-caractères peuvent encoder au maximum les tables du type latin-1 ?

  • A : 127
  • B : 128
  • C : 255
  • D : 256

UNICODE est

  • A : un encodage qui remplace maintenant latin-1
  • B : une simple association d'un entier et d'un caractère
  • C : un encodage sur 1 octet
  • D : un encodage sur 4 octets

UTF-8 est

  • A : une autre façon d'encoder les tables 1 octet (donc 8 bits)
  • B : un encodage basé sur UNICODE dans lequel les caractères prennent de 8 octets
  • C : la 8e version de l'encodage UTF, incluant le symbole monétaire EURO
  • D : un encodage basé sur UNICODE dans lequel les caractères prennent de 1 à 4 octets

Voilà pour l'encodage. C'est une source d'erreur très courante dans les programmes, d'où la nécessité d'en parler.

La prochaine activité Python fera juste le point sur l'enregistrement des fichiers-texte et nous pourrons voir l'influence de l'encodage sur la taille des enregistrements en enregistrant un même texte sous différents enregistrements.

Activité publiée le 09 02 2020
Dernière modification : 11 05 2020
Auteur : ows. h.