Le mojibake du cours (é au lieu de é) se reproduit en trois lignes
Exercice de TD · niveau 3 (difficile) · NSI (première), chapitre 2 — Flottants, booléens et textes · Caractères, encodages et accidents de lecture
Énoncé
Le mojibake du cours (é au lieu de é) se reproduit en trois lignes. (a) Écrire abimer(texte) qui simule la lecture d'un fichier UTF-8 par un logiciel croyant lire du latin-1. (b) Écrire reparer(abime) qui annule l'accident, et expliquer pourquoi c'est possible. (c) Pourquoi l'accident ne provoque-t-il aucune erreur, alors que l'accident inverse (lire du latin-1 en croyant lire de l'UTF-8) en provoque souvent une ?
Corrigé
(a) et (b). Un encodage transforme un texte en octets, un décodage fait l'inverse. L'accident consiste à enchaîner l'encodage d'un encodage et le décodage d'un autre.
def abimer(texte):
"""Simule : ecrit en UTF-8, relu comme du latin-1."""
return texte.encode("utf-8").decode("latin-1")
def reparer(abime):
"""Annule abimer : chaque caractere redevient son octet."""
return abime.encode("latin-1").decode("utf-8")
>>> abimer("élève")
'élève'
>>> reparer(abimer("élève"))
'élève'
La réparation marche parce que les deux fonctions sont exactement inverses : latin-1 associe le caractère de point de code à l'octet , pour les valeurs, sans trou. Ré-encoder en latin-1 restitue donc les octets UTF-8 d'origine, octet pour octet, et il ne reste qu'à les décoder correctement.
(c) L'asymétrie. Latin-1 sait décoder n'importe quelle suite d'octets : les valeurs possibles ont toutes un caractère. Aucune erreur n'est donc possible — seulement un résultat absurde. UTF-8, au contraire, a une grammaire : un octet commençant par 110 doit être suivi d'un octet commençant par 10. Une suite d'octets latin-1 quelconque viole vite cette règle, et le décodage lève UnicodeDecodeError.
La leçon : un décodage qui réussit ne prouve rien. Ce n'est pas le programme qui sait dans quel encodage un fichier est écrit ; c'est vous. D'où l'argument encoding obligatoire dans open.
Contrôle : "élève".encode("latin-1").decode("utf-8") lève bien UnicodeDecodeError, tandis que l'accident dans l'autre sens passe sans broncher.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.