NSI 1ère

Représentation des caractères en mémoire

Fichiers texte purs

L'écrit étant un moyen de choix pour conserver et échanger de l'information, l'idée de coder les symboles utilisés pour écrire surgit assez naturellement lorsqu'il est question de traitement d'information.

Le problème qui nous intéresse ici est bien celui du codage de l'écriture, en excluant tout ce qui concerne la mise en forme du texte contribuant l'aspect visuel d'un document (police, graisse, inclinaison, taille et couleur des caractères, indications de mise en page...).

Un logiciel permettant de créer et modifier des fichiers ne contenant que du texte est appelé un éditeur de texte pur (ou texte brut). C'est le cas du bloc-note sous Windows, de Gedit sous Linux, de Notepad++...

Si un fichier est un fichier texte pur, alors un éditeur de texte pur doit pouvoir afficher toute l'information contenue dans le fichier sous la forme d'un texte lisible, sous réserve d'être correctement configuré.

Les fichiers du web (html, javascript, css) ainsi que les fichiers de code source des programmes (comme les fichiers de code python d'extension .py) sont des fichiers texte pur.

Le Jeu Universel de Caractères (JUC, ou UCS)

Un ordinateur, un appareil numérique, ne savent traiter l'information que sous la forme de nombres. Un principe simple pour représenter les caractères consiste donc à désigner chaque caractère par un code numérique entier positif. De nombreuses tables de codage, fixant une correspondance entre caractère et valeur numérique, ont ainsi été créés, par dfférentes entreprises et pour différents alphabets.

Tant que l'information ainsi codée n'est destinée qu'à circuler en interne dans une entreprise, un pays, ou à n'être exploitée que sur un type de machine, la coexistence de nombreux systèmes différents ne pose pas de problèmes.
Cependant, la multiplication des échanges d'information sous forme électronique a fait apparaître l'intérêt d'un système d'encodage universel, permettant d'encoder n'importe quel caractère d'écriture, et interprétable par toutes les machines. Ce système appelé Unicode repose sur la notion de Jeu Universel de Caractères (JUC, ou UCS pour Universal Character Set.)

Aperçu historique : d'ASCII à UCS/Unicode

Systèmes de code propriétaires et nationaux.

L'élaboration du Jeu Universel de Caractère ne s'est pas faite du jour au lendemain, et son utilisation n'est pas encore totalement généralisée. Il est donc intéressant, pour comprendre certains problèmes que l'on peut rencontrer, de connaître les autres systèmes utilisés historiquement.

ASCII standard

L'ASCII est un exemple de code utilisé historiquement et très répandu. ASCII signifie "American Standard Code for Information Interchange".

L'ASCII était codé sur 7 bits, ce qui permettait de coder 27=128 caractères, soient les lettres de l'alphabet en majuscule et minuscule, les chiffres, les principaux caractères de ponctuation et quelques symboles, notamment mathématiques. Certains codes n'étaient pas attribués à des caractères, mais constituaient des codes de contrôle permettant d'envoyer des commandes comme 'saut de ligne', 'saut de page', 'retour arrière', 'signal sonore'... Les codes 0 et 127 n'étaient pas attribués, pour des raisons pratiques liées notamment à l'emploi de cartes perforées pour entrer l'information.

En l'absence de caractères spéciaux comme é,ç..., l'ASCII était bien adapté pour écrire l'anglais, mais pas vraiment pour les langues utilisant d'autres caractères, comme le français.

ASCII "étendu" et systèmes dérivés

L'octet étant l'unité de base de mesure de la quantité d'information, on dispose avec un octet de quoi coder 28=256 caractères, soient 128 de plus que les 128 valeurs utilisées par l'ASCII. Plusieurs systèmes ont donc "étendu" l'ASCII standard, en lui ajoutant des caractères supplémentaires sur la plage de codes 128 à 255. Cette extension s'est faite au départ de manière un peu anarchique, différentes entreprises définissant différentes tables.

Premières avancées vers l'internationalisation et la normalisation

Les pages de codes ISO-8859

La nécessité d'harmoniser et de normaliser les différents systèmes a conduit i'Internation Standards Organisation (ISO) à définir dans une norme internationale, ISO-8859, un certain nombre de jeux de caractères codés sur un octet, appelés couramment "pages de codes". Chaque "page de codes" contient le code ASCII de base, mais ajoute des caractères supplémentaires adaptés à l'écriture d'un groupe de langues : par exemple la page de codes ISO-8859-1 (latin-1), ou sa variante plus récente ISO-8859-15 (latin-9) , contiennent la plupart des caractères nécessaires pour écrire correctement de nombreuses langues d'Europe de l'Ouest.

Un octet ne suffit pas toujours

La norme ISO-8859 permettait de coder, dans différentes pages de code, des caractères appartenant à différents alphabets (grec, cyrillique, arabe...), toujours en utilisant un octet par caractère.

Cependant des langues comme le chinois ou le japonais s'écrivent en utilisant bien plus de 256 symboles différents. Des normes différentes ont donc été élaborées pour écrire ces langage, utilisant cette fois deux octets par caractère, ce qui permet de coder jusqu'à 216=65536 caractères différents.

Un foisonnement de normes différentes

Le système des pages de code engendre une certaine complexité : un fichier texte encodé selon la norme ISO-8859 ne peut être affiché correctement que si le logiciel charge la bonne page de code... Pour ne rien simplifier, Microsoft a créé pour Windows des versions légèrement modifiées de la norme ISO. Le jeu de caractères Windows adapté aux langues d'Europe de l'Ouest est Windows-1252, ou WIndows occidental. Et s'il était besoin de rendre les choses encore plus obscures, sous Windows ces encodages sont également souvent désignés par le nom "ANSI"...

Pour avoir une idée de la complexité engendrée par cette multitude des pages de code, on peut sous Windows lancer le programme charmap (table des caractères). Ce programme montre les caractères affichables par ordre de code croissant (choisir une police de caractères assez générique comme Arial). En cochant la case 'affichage avancé', on fait apparaître une boîte de sélection proposant, outre Unicode qui est maintenant le jeu par défaut, un certain nombre d'autres jeux de caractères. Le matricule unicode du caractère sélectionné apparaît dans la barre d'état en bas de la fenêtre, précédé de la lettre U. A côté, entre parenthèses, aparaît le code dans le jeu de caractère (page de codes) sélectionné. Tous les codes sont écrits en hexadécimal.

table des caractères

Unicode et norme ISO-10646

Un unique jeu de caractères

Le système Unicode est né au début des années 1990 d'une volonté de simplification et d'unification du système des pages de codes, devenu beaucoup trop complexe et source d'erreurs et de vulnérabilités. Il est aujourd'hui défini par une norme reconnue internationalement (norme ISO-10646) et développé par le consortium Unicode.

Le principe de base consiste à répertorier dans un seul registre tous les caractères d'écriture employés sur Terre (y compris dans le passé), et à attribuer à chaque caractère de ce Jeu Universel de Caractères (JUC/UCS) un matricule unique, qu'on appelle son point de code Unicode.

Les valeurs des points de code Unicode sont comprises entre 0 et (10FFFF)16, ce qui représente un peu plus d'un million de possibilités. Tous les codes ne sont cependant pas attribués à un caractère ou à un signe. Même ainsi, cela laisse de quoi représenter un très grand nombre de symboles différents. La norme est ainsi actualisée chaque année, avec l'introduction de nouveaux symboles (par exemple de nouveaux Emoji...)

Le système Unicode/ISO-10646 repose sur un répertoire unique contenant tous les caractères d'écriture utilisés sur Terre, le Jeu Universel de Caractères JUC (Universal Character Set USC)

Les systèmes d'encodage Unicode

Les valeurs des codes Unicode, appelés points de code, ne sont pas directement utilisées dans les fichiers texte, pour différentes raisons : optimisation de l'espace mémoire, compatibilité avec les systèmes précédents. L'encodage proprement dit consiste à traduire le point de code Unicode en une série de bits, suivant un protocole normalisé. Ces protocoles sont appelées UTF pour "UCF Transformation Format".

La méthode d'encodage la plus utilisée pour les langues occidentales, en particulier sur Internet, est appelée UTF-8. Dans ce format, la taille minimale utilisable pour coder un caractère ("codet") est de 1 octet, mais un caractère peut occuper 2,3 et jusqu'à 4 octets. UTF-8 est conçu pour que les symboles les plus utilisés dans les langues occidentales (chiffres, lettres de l'alphabet) n'occupent qu'un seul octet, de façon a économiser l'occupation de la mémoire et de la bande passante. Ce parti-pris permet aussi la compatibilité amont avec l'ASCII : un fichier ne contenant que des caractères du jeu ASCII sera encodé exactement de la même façon en ASCII et en UTF-8.

On peut citer parmi les autres formats d'encodage pour Unicode l'UTF-16, dans lequel un caractère peut occuper soit 2 octets, soit 4 octets, et l'UTF-32, dans lequel tous les caractères sont codés sur 4 octets. UTF-16 est souvent abusivement dénommé "UCS" ou "UCS-2" sous Windows, où il a été largement utilisé. UTF-32 est réservé à des usages spécifiques car il "gaspille" beaucoup d'espace mémoire.

Les points de code Unicode une fois encodés occupent, selon leur valeur et l'encodage utilisé, entre 1 à 4 octets en mémoire

Problèmes résiduels dus à la coexistence des encodages

Il devient heureusement de plus en plus rare d'être confronté à un problème d'encodage, mais cela peut encore arriver.
    Les problêmes surviennent avec les caractères accentués ou « spéciaux » comme ç, Ø , ï, ± , €... comme le montrent les deux cadres ci-dessous, dans lesquels cette même phrase est décodée :
  • à gauche, comme de l'ISO-8859-1 alors qu'elle a été encodée en UTF-8
  • à droite, comme de l'UTF-8, alors qu'elle a été encodée en ISO-8859-1

Pour vérifier que ces affichages étranges sont bien dus à un problème d'encodage, sous Firefox on peut ouvrir le contenu de chacun des cadres seul dans une nouvelle page(clic-droit sur le cadre,"ce cadre","afficher ce cadre seul" ou "ouvrir ce cadre dans un nouvel onglet') . Ouvrir ensuite le menu de Firefox, choisir "Plus" puis "encodage du texte" : on obtient une liste des (très nombreux) encodages gérés par Firefox. Pour une page web qui n'aurait pas déclaré son encodage et dont l'encodage n'aurait pas été détecté automatiquement par Firefox, ce menu permet de corriger le problème, comme on peut le vérifier ici en basculant entre "Unicode" et "Occidental".

Cette fonctionnalité de choix de l'encodage, autrefois très utile dans un navigateur, tend à disparaître des versions récentes (Chrome et Edge ne la proposent déjà plus) car la généralisation de l'utilisation d'UTF-8 sur le Web la rend peu à peu inutile.

Les éditeurs de texte pur permettent généralement un choix d'encodages (dont utf-8) lors de l'enregistrement d'un texte. Un éditeur aussi simple que le bloc-note de Windows propose déjà trois encodages différents, tandis que Notepad++ par exemple en permet une petite cinquantaine.

L'utilisation d'un encodage non-Unicode basé sur le principe des pages de codes, peut être signalé par les termes ISO-8859, Occidental, Windows (ou Win), Dos, ANSI... tandis que toute appellation contenant la lettre U (UCS, UTF-8 , Unicode...) fait référence à un encodage de la famille Unicode
L'affichage de caractères ou symboles inattendus par un navigateur Web ou un éditeur de texte pur, provient généralement d'un problème d''encodage.

Encodage de texte et Python

Python et Unicode

La fonction Python  chr   permet d'obtenir le caractère correspondant à un code Unicode. Par exemple, en écrivant dans la console :

    >>>chr(70)
on obtient le caractère 'F', dont le code Unicode est 70 (en base 10).

Si l'entier passé en paramètre à la fonction chr  n'a pas de caractère Unicode correspondant,  chr  retourne une chaîne de caractères composée du "caractère d'échappement", "\", suivi généralement du code en hexadécimal. Pour quelques codes de contrôle, chr renvoie plutôt leur signification : il s'agit des codes 9 ('\t' comme tabulation), 10 ('\n' comme new line) et 13 ('\r' comme return carriage).

La fonction Python  ord   permet d'obtenir le code (en base 10) d'un caractère. Par exemple, en écrivant dans la console :

    >>>ord('&')
on obtient le code de l'esperluette &, qui s'écrit 38 en base 10.

Lecture de fichier texte en Python

Python fournit des méthodes pour lire les fichiers texte avec un encodage donné, et pour lire le contenu d'un fichier sous la forme d'une suite d'octets. Le programme suivant utilise certaines de ces méthodes pour lire de petits fichiers de texte pur et afficher le contenu du fichier octet par octet, en base seize.
  1. ####################### constantes à modifier #################################
  2. #chemin d'accès au dossier contenant le fichier texte à analyser :
  3. chemin="C:/Users"
  4. #nom du fichier à analyser :
  5. fichier="alpha_utf8.txt"
  6. #encodage à utiliser pour lire le fichier. Valeurs possibles "utf-8","cp1252","iso-8859-15"
  7. encodage="utf-8"
  8. #################### fin de la partie modifiable ###############################
     
  9. import os,io
  10. os.chdir(chemin)
  11. f=open(fichier,encoding=encodage)
  12. contenu=f.read(None)
  13. f.close()
  14. print("texte :",contenu)
  15. print("nombre de caractères affichables (espaces inclus):",len(contenu))
  16. print("encodage : ",encodage)
  17. f=open(fichier,'rb')
  18. contenu=[]
  19. while True:
  20.     c=f.read(1)
  21.     if c==b'':
  22.         break
  23.     rc=hex(ord(c))[2:]
  24.     rc='0'*(2-len(rc))+rc
  25.     contenu.append(rc)
  26. f.close()
  27. print("\nnombre d'octets :",len(contenu))
  28. print("encodage en hexadécimal :")
  29. print(' '.join(contenu))

Télecharger les fichiers ci-dessous en faisant un clic-droit sur le lien puis en choisissant "enregistrer la cible du lien sous". Sachez dans quel dossier vous avez enregistré les fichiers...

Copier/coller le code ci-dessus dans l'éditeur Python.

Dans le code source du programme, changer la valeur de la variable chemin: ce doit être le chemin du dossier où les fichiers à analyser sont enregistrés. Il est possible de copier-coller ce chemin depuis la barre d'adresses de l'explorateur de fichiers. Sous Windows il faudra remplacer tous les \ par des \\ ou des /. La variable fichier doit contenir le nom du fichier que l'on veut analyser, et la variable "encodage" le nom de l'encodage à utiliser ("utf-8", "iso-8859-15" ou "cp1252"). Toutes ces variables sont des chaines de caractères, il faut donc écrire leurs valeurs entre " " ou ' '.

A l'aide du programme et des fichiers texte téléchargés, étudier les compatibilités et incompatibilités entre les encodages UTF-8, ISO-8859-15 et Win1252. On pourra notamment vérifier que les fichiers proposés sont bien des fichiers de texte pur, s'interroger pour chaque encodage sur les valeurs que peut prendre un octet, au nombre d'octets par caractère, aux similitudes et différences entre les valeurs des codes pour un même caractère, et proposer une explication aux problèmes d'affichage rencontrés lorsqu'un fichier texte est lu avec un encodage différent de celui avec lequel il a été enregistré (ex: fichier utf-8 lu comme un fichier iso-8859-15).

▶ Prévoir de présenter les conclusions à l'oral.