Hors programme, en lien avec le projet du chapitre
Exercice supplémentaire · niveau 3 (difficile) · NSI (première), chapitre 2 — Flottants, booléens et textes · Unicode dans le détail
Énoncé
Hors programme, en lien avec le projet du chapitre. Écrire deviner_encodage(octets) qui essaie "utf-8" puis "latin-1" et rend le premier qui réussit. Montrer, sur un exemple, que la fonction se trompe toujours dans un cas précis, et proposer un critère supplémentaire.
Corrigé
def deviner_encodage(octets, candidats=("utf-8", "latin-1")):
"""Premier encodage candidat qui decode sans lever d'exception.
Precondition : aucune. ATTENTION, la valeur rendue est une
HYPOTHESE, jamais une certitude : voir la discussion.
"""
for e in candidats:
try:
octets.decode(e)
return e
except UnicodeDecodeError:
pass
return None
>>> deviner_encodage("élève".encode("utf-8"))
'utf-8'
>>> deviner_encodage("élève".encode("latin-1"))
'latin-1'
>>> deviner_encodage(b"\xff\xfe\xfd")
'latin-1'
Ce qui marche. L'ordre des candidats n'est pas arbitraire : de l'UTF-8 valide est rarement du latin-1 plausible, alors que du latin-1 est toujours accepté par le décodeur. On essaie donc le plus exigeant d'abord.
Le cas où la fonction se trompe toujours. La troisième ligne le montre : FF FE FD n'est ni du texte ni rien d'intelligible, et la fonction répond « latin-1 » avec aplomb. La raison est structurelle — latin-1 décode n'importe quelle suite d'octets, ses codes étant tous attribués. Le dernier candidat de la liste sera donc toujours retenu, quoi qu'on lui donne. La fonction ne devine rien : elle élimine.
Un critère supplémentaire, comme le suggère le projet du cours : une fois le décodage réussi, mesurer la vraisemblance du texte obtenu — proportion de caractères imprimables, présence de séquences typiques du mojibake (é, è, ê), fréquence des lettres comparée à celle du français. On passe alors d'un test binaire à un score, et l'on choisit le maximum.
Prolongement honnête : aucun détecteur n'est infaillible, et c'est démontrable. Le même octet E9 est un é en latin-1 et une lettre parfaitement valide dans d'autres tables ISO-8859 ; sans information extérieure, rien ne permet de trancher. C'est bien pourquoi les formats modernes déclarent leur encodage — l'en-tête HTTP, la balise <meta charset>, et l'argument encoding de open.
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.